Explains the encryption algorithm used by vendor daemons for reporting. |
flexlm | |
by fravia+ | ||
fra_00xx 980926 Nolan Blender 0100 FL PC | You may be able to read a very interesting example of team-reversing (about FlexLM) here Nolan Blender writes in the following essay: | |
Program initially contains #include<stdio.h> main(int argc, char *argv[]) { } % make cc -Ae -g democrack.c -c cc -Ae -g democrack.o lsfilter.o -o democrack /usr/ccs/bin/ld: Unsatisfied symbols: ls_replog_ascii (data) ls_log_repfile_client (data) ls_replog_clear (data) ls_log_repfile (data) ls_client_send_str (code) *** Error exit code 1At this point, we know that there are a whole bunch of variables and subroutines messing. My tactic here is to start debugging the license server, and to declare the missing variables as initialized data space.
0000EA68 ls_append_repfile+00ac STB %r0,R'590(%r1) 0000EA6C ls_append_repfile+00b0 ADDIL L'0,%r27 0000EA70 ls_append_repfile+00b4 STW %r0,R'3c(%r1) 0000EA74 ls_append_repfile+00b8 ADDIL L'2800,%r27 0000EA78 ls_append_repfile+00bc STW %r0,R'528(%r1) 0000EA7C ls_append_repfile+00c0 LDW -420(%r30),%r26 0000EA80 ls_append_repfile+00c4 LDIL L'6800,%r31 0000EA84 ls_append_repfile+00c8 BLE R'3e8(%sr4,%r31) 0000EA88 ls_append_repfile+00cc COPY %r31,%r2The program counter is at EA84 at this frame. I looked at the preceeding frames, and I saw that only r26 was set, and that r25 was not set.
dde> regs r0: 00000000 00000000 0000EA8F r3 : 7B03AE50 00000007 7B03A51C 7B03A53C r7 : 7B03A650 7B03A650 00000020 0000001F r11: 00000000 00000000 08000084 08000084 r15: 00000000 00000001 4001CAF0 00000008 r19: 00000040 00000040 00000108 00004000 r23: 00000141 008C0A00 00000000 7B03F118 r27: 4000A610 00000040 00000008 7B03F550 r31: 0000EA8F pc: 0000EA8C ipsw: 0006000F sar: 00000018So r26 contains 7b03f118 - a pointer to some sort of automatic variable on the stack. Let's check it out:
dde> dump -from 7b03f118 -to 7b03f128 7B03F118: 2320464C 45586C6D 20526570 6F727420 7B03F128: 4C6F672CThat sure looks like character data to me!
dde> dump -from 7b03f118 -to 7b03f128 -char 7B03F118: "# FL" "EXlm" " Rep" "ort " 7B03F128: "Log,"After checking a few more runs, it was clear that the input was a null terminated string. So now we can start to fill in the blanks in our "main" program.
#include<stdio.h> int ls_replog_ascii[6]; int ls_log_repfile_client; int ls_replog_clear[6]; FILE *ls_log_repfile; extern char *cksumbuf; /* declared in lsfilter.o */ extern long buflen; extern char *curra; main(int argc, char *argv[]) { ls_lf("# FLEXlm Report Log, 8 September, 1999, \"blenderd\" on \"myhost\"\n" ); ls_lf("5 2 2\n"); ls_lf("7 1 78261234\n"); ls_lf("23 1\n"); ls_lf("7 2 hp700_u9\n"); ls_lf("27 2\n"); ls_lf("7 3 /usr/tmp/lockblenderd\n"); } int ls_client_send_str() { printf ("ls_client_send_str called.\n"); }Attempting to compile and run this program gets a segmentation fault. Some of these variables need to be initialized. The next step is to run DDE against our program. The program will fault, and we can then look at the assembly listing to see what made it fault.
Break at: \\democrack\main\12 dde> go Intercepted event type 'signal SIGSEGV'. Signal received by process 11165: (SIGSEGV). Stopped at: ls_lf+0254 (000038B8) Assembly shows us: 000038A0 ls_lf+023c LDW -1044(%r30),%r31 000038A4 ls_lf+0240 LDO R'1(%r31),%r19 000038A8 ls_lf+0244 STW %r19,-1044(%r30) 000038AC ls_lf+0248 ADDIL L'5800,%r27 000038B0 ls_lf+024c LDO R'2f8(%r1),%r20 000038B4 ls_lf+0250 LDW -1044(%r30),%r21 000038B8 ls_lf+0254 LDWX,S %r21(%r20),%r22 000038BC ls_lf+0258 COMIBF,= -1,%r22,ls_lf+021c regs are r0: 00000000 40006B10 0000386B r3 : 7B03A000 00000001 7B03A4F0 7B03A4F8 r7 : 7B03A608 7B03A608 00000020 0000001F r11: 00000000 00000000 08000084 08000084 r15: 00000000 00000001 4001CA20 00000002 r19: 0000007E 40006E08 0000007E 00000000 r23: 00000000 00000001 4000117A 00000000 r27: 40001310 00000001 00000001 7B03AAC8 r31: 0000007D pc: 000038B8 ipsw: 0006000F sar: 00000008 Looking at r20 we see dde> dump -from 40006e08 40006E08: 00000000So it sure looks like an attempt to reference through a bad pointer. In this case we're lucky - this is one of the variables which we're looking for. To get the values, we need to look at the vendor daemon again though.
% nm -x democrack | grep 6e08 ls_replog_ascii |0x40006e08|extern|data |$BSS$ 0.1u 0.0s 0:00 11%The offset was at ls_lf+254, so now we must hunt down that offset in the vendor daemon.
In our daemon directory: % nm -x blenderd | grep ls_lf ls_lf |0x00005db0|extern|code |$CODE$So we can figure out the equivalent offset in that routine in the vendor daemon. It's the base address+offset.
dde> print 0x5db0+0x254 -hex 0x0000000000006004We start up the vendor daemon again, and set a breakpoint at that offset.
% /opt/langtools/bin/dde ./blenderd -T myhost 6.1 3 -c test.dat Once dde has started dde> break `va(6004) Breakpoint(s) set. dde> go Break at: ls_lf+0254 (00006004)We look at the assembly code again, and we can see that we're in the same spot in ls_lf, just that everything should be OK at this point.
dde> regs r0: 00000000 40005E10 00005FB7 r3 : 7B03AE50 00000007 7B03A51C 7B03A53C r7 : 7B03A650 7B03A650 00000020 0000001F r11: 00000000 00000000 08000084 08000084 r15: 00000000 00000001 4001CAF0 00000008 r19: 00000001 40006320 00000001 00000006 r23: 00000000 00000001 400010C2 00000000 r27: 4000A610 00000001 00000001 7B03F9D0 r31: 00000000 pc: 00006004 ipsw: 0004000F sar: 0000001B Looking at that location, it appears as though this is a pointer to an array of ints. dde> dump -from 40006320 -to 40006340 40006320: 00000006 00000007 00000019 00000025 40006330: 00000026 FFFFFFFF 0A000000 0A000000 40006340: 0A000000 After tracing the program a bit, we find that it uses -1 as a terminator, thus we can initialize the array and quit at the first -1. ls_replog_ascii[0] = 0x00000006; ls_replog_ascii[1] = 0x00000007; ls_replog_ascii[2] = 0x00000019; ls_replog_ascii[3] = 0x00000025; ls_replog_ascii[4] = 0x00000026; ls_replog_ascii[5] = 0xffffffff; After a few hours of digging around looking at variables, the following program can be had: #include<stdio.h> int ls_replog_ascii[6]; int ls_log_repfile_client; int ls_replog_clear[6]; FILE *ls_log_repfile; extern char *cksumbuf; /* declared in lsfilter.o */ extern long buflen; extern char *curra; main(int argc, char *argv[]) { char instr[1024]; char outstr[1024]; char instr1[1024]; int num; int i; unsigned long testval; unsigned long *fooval; FILE *fp; char ibuf[1024]; ls_log_repfile = stdout; ls_replog_ascii[0] = 0x00000006; ls_replog_ascii[1] = 0x00000007; ls_replog_ascii[2] = 0x00000019; ls_replog_ascii[3] = 0x00000025; ls_replog_ascii[4] = 0x00000026; ls_replog_ascii[5] = 0xffffffff; ls_replog_clear[0] = 0x00000005; ls_replog_clear[1] = 0xffffffff; ls_replog_clear[2] = 0x000001f3; ls_replog_clear[3] = 0x00000000; ls_replog_clear[4] = 0x00000000; ls_replog_clear[5] = 0x00000000; ls_log_repfile_client = 0; curra = (char *) malloc(1024 * sizeof(char)); cksumbuf = (char *) malloc(1024 * sizeof(char)); buflen = 0; strcpy(curra, ""); ls_lf("# FLEXlm Report Log, 8 September, 1999, \"blenderd\" on \"myhost\"\n" ); ls_lf("5 2 2\n"); ls_lf("7 1 78261234\n"); ls_lf("23 1\n"); ls_lf("7 2 hp700_u9\n"); ls_lf("27 2\n"); ls_lf("7 3 /usr/tmp/lockblenderd\n"); ls_flush_replog(stdout); fflush(stdout); } int ls_client_send_str() { printf ("ls_client_send_str called.\n"); return(0); }Now we are ready to start reversing the actual code. The procedure that I use to reverse engineer programs is to build a program that duplicates the behaviour of the target routines.
dde> dump -from 40001191 -to 400011c1 -char 40001191: "Copy" "righ" "t_19" "88-1" 400011A1: "996_" "Glob" "etro" "tter" 400011B1: "_Sof" "twar" "e_In" "c_\0\377" 400011C1: "\377\377\377\377"Which gives us the string that it's stepping through.
char *cstring="Copyright_1988-1996_Globetrotter_Software_Inc_"; int incr_currc(void) { if (chptr == 0) { /* incr_currc+8 */ chptr = cstring; } else { chptr++; if (chptr >= cstring+46) { chptr = cstring; } } return(0); }Now after understanding a little bit about how the incr_currc works, we can turn our attention to the encryption algorithm itself. There are a couple of things which seem relatively straightforward in C, but become somewhat warped and more difficult to understand after compilation.
4028 filter+0254 ADDIL L'fffff800,%r27 402C filter+0258 LDW R'694(%r1),%r21 4030 filter+025c SH2ADD %r21,%r21,%r22 4034 filter+0260 SH2ADD %r22,%r0,%r31 4038 filter+0264 ADDIL L'fffff800,%r27 403C filter+0268 LDW R'690(%r1),%r19 4040 filter+026c ADD %r19,%r31,%r20 4044 filter+0270 ADDIL L'fffff800,%r27 4048 filter+0274 LDW R'698(%r1),%r21 404C filter+0278 SH2ADD %r21,%r21,%r22 4050 filter+027c SH2ADD %r22,%r0,%r1 4054 filter+0280 SH2ADD %r1,%r1,%r31 4058 filter+0284 SH2ADD %r31,%r0,%r19 405C filter+0288 ADD %r20,%r19,%r20 4060 filter+028c STW %r20,-52(%r30)This actually translates to code similar to what follows.
/* This is globally declared. */ int sv_690[4]; /* local storage for filter() */ /* Local variable */ int fv_d; /* offset -52 */ fv_d = sv_690[0] + sv_690[1] * 20 + sv_690[2] * 400;Algorithm reversing.
#include<stdio.h> #include<string.h> char initvals[] = { 0x37, 0x5a, 0x4b, 0x43, 0x71, 0x36, 0x34, 0x29, 0x5e, 0x21, 0x79, 0x45, 0x5b, 0x7a, 0x5f, 0x7d, 0x59, 0x2b, 0x46, 0x66, 0x53, 0x42, 0x3a, 0x3b, 0x76, 0x3d, 0x25, 0x74, 0x58, 0x63, 0x38, 0x26, 0x35, 0x73, 0x40, 0x28, 0x27, 0x47, 0x23, 0x61, 0x6d, 0x41, 0x7c, 0x68, 0x4d, 0x44, 0x7e, 0x52, 0x51, 0x39, 0x55, 0x67, 0x30, 0x4e, 0x69, 0x4a, 0x3f, 0x24, 0x5d, 0x4c, 0x56, 0x6f, 0x50, 0x60, 0x6e, 0x2d, 0x5c, 0x72, 0x2a, 0x49, 0x75, 0x31, 0x3e, 0x6b, 0x70, 0x7b, 0x22, 0x2f, 0x64, 0x33, 0x54, 0x78, 0x3c, 0x4f, 0x48, 0x2e, 0x20, 0x6c, 0x2c, 0x62, 0x32, 0x57, 0x77, 0x65, 0x6a, 0x00 }; int main() { char *p; char outarr[256]; /* 256 different mappings */ int i; int outindex; /* index into output table */ int numindex = 0; /* the current index into initvals table */ /* * "unused" values will have 255 for easier fault * detection. */ for (i = 0; i <256; i++) { outarr[i] = 0xff; } /* * step through the initvals array, and load * the location in outarr with the index into * the initvals array. */ for (p = initvals; *p != '\0'; p++) { outindex = (int) *p & 0xff; outarr[outindex] = numindex; numindex++; } /* load the last value anyway, probably not used */ outindex = (int) *p & 0xff; outarr[outindex] = numindex; /* dump the array to stdout */ printf ("char * decodetable[] = {"); for (i = 0; i < 256; i++) { if (i%8==0) { printf ("\n "); } printf ("%02x", outarr[i] & 0xff); if (i != 255) { printf (", "); } } printf ("\n};\n"); }Here we have the completed encrypt/decrypt (for ascii_filter). Sections which should be examined in depth are the inner for loops in the encrypt and decrypt sections.
#include<stdio.h> #include<string.h> /* Now internal */ char tstbuf[1024]; /* 40002978? */ char *curra; /* In other at 40006ee4 */ int curoffset; /* current offset value */ /* * This is the encryption values array */ char initvals[] = { 0x37, 0x5a, 0x4b, 0x43, 0x71, 0x36, 0x34, 0x29, 0x5e, 0x21, 0x79, 0x45, 0x5b, 0x7a, 0x5f, 0x7d, 0x59, 0x2b, 0x46, 0x66, 0x53, 0x42, 0x3a, 0x3b, 0x76, 0x3d, 0x25, 0x74, 0x58, 0x63, 0x38, 0x26, 0x35, 0x73, 0x40, 0x28, 0x27, 0x47, 0x23, 0x61, 0x6d, 0x41, 0x7c, 0x68, 0x4d, 0x44, 0x7e, 0x52, 0x51, 0x39, 0x55, 0x67, 0x30, 0x4e, 0x69, 0x4a, 0x3f, 0x24, 0x5d, 0x4c, 0x56, 0x6f, 0x50, 0x60, 0x6e, 0x2d, 0x5c, 0x72, 0x2a, 0x49, 0x75, 0x31, 0x3e, 0x6b, 0x70, 0x7b, 0x22, 0x2f, 0x64, 0x33, 0x54, 0x78, 0x3c, 0x4f, 0x48, 0x2e, 0x20, 0x6c, 0x2c, 0x62, 0x32, 0x57, 0x77, 0x65, 0x6a, 0x00}; /* * This is the decryption values array. Invalid values * are set to ff */ char decodetable[] = { 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x56, 0x09, 0x4c, 0x26, 0x39, 0x1a, 0x1f, 0x24, 0x23, 0x07, 0x44, 0x11, 0x58, 0x41, 0x55, 0x4d, 0x34, 0x47, 0x5a, 0x4f, 0x06, 0x20, 0x05, 0x00, 0x1e, 0x31, 0x16, 0x17, 0x52, 0x19, 0x48, 0x38, 0x22, 0x29, 0x15, 0x03, 0x2d, 0x0b, 0x12, 0x25, 0x54, 0x45, 0x37, 0x02, 0x3b, 0x2c, 0x35, 0x53, 0x3e, 0x30, 0x2f, 0x14, 0x50, 0x32, 0x3c, 0x5b, 0x1c, 0x10, 0x01, 0x0c, 0x42, 0x3a, 0x08, 0x0e, 0x3f, 0x27, 0x59, 0x1d, 0x4e, 0x5d, 0x13, 0x33, 0x2b, 0x36, 0x5e, 0x49, 0x57, 0x28, 0x40, 0x3d, 0x4a, 0x04, 0x43, 0x21, 0x1b, 0x46, 0x18, 0x5c, 0x51, 0x0a, 0x0d, 0x4b, 0x2a, 0x0f, 0x2e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; /* * String containing array used for marking ascii_filter or filter */ char *cstring="Copyright_1988-1996_Globetrotter_Software_Inc_"; unsigned long myedata; /* in other at 400013d8 */ /* * pointer to current value in cstring */ char *chptr; /* in other at 400013dc */ main() { char mystr[1024]; char encstr[1024]; char decstr[1024]; nb_crypt_init(); mystr[0] = *chptr; /* Load in the test string. */ strcpy(mystr+1,"The quick brown fox jumps over the lazy dog. 1234567890\n"); printf ("orig : %s\n", mystr); /* encrypt the string */ nb_encrypt(mystr, encstr); printf ("encstr: %s\n", encstr); nb_crypt_init(); nb_decrypt(encstr, decstr); printf ("decstr: %s\n", decstr); } /*----------------------------------------------------------* * * nb_crypt_init() * * initialize pointers and variables. This is a consolidation * of various initializations. *----------------------------------------------------------*/ int nb_crypt_init() { myedata = 0; chptr = 0; curra = 0; curoffset = 0; if (curra == 0) { curra = tstbuf; } if (tstbuf[0] == '\0') { strcpy(tstbuf, initvals); strcat(tstbuf, initvals); } if (chptr == 0) { chptr = cstring; } return(0); } /*----------------------------------------------------------* * * nb_encrypt *----------------------------------------------------------*/ int nb_encrypt (char *instr, char *outstr) { char *asval_a; /* offset -100 */ char *asval_b; /* offset -56 */ int asval_c; /* offset -52 */ asval_a = instr; /* Determine if we were in filter mode before */ if (myedata != 1) { /* ascii_filter+0x78 */ if (myedata != 0) { /* ascii_filter+0x94 */ incr_currc(); } myedata = 1; } outstr[0] = chptr[0]; /* step the firstchar pointer */ incr_currc(); /* Copy string to output; first char reserved for indicator */ strcpy(outstr+1, instr); for (asval_b = outstr+1; *asval_b != '\0'; asval_b++) { /* Ignore low nonprintable chars */ if (*asval_b >= ' ') { /* * Since the ascii range we are converting * starts at 32, subtract 32 to have range * start at 0 */ asval_c = (*asval_b) - 32; /* * Use the encrypting table to find a corresponding value * and set the output char to that value */ *asval_b = curra[asval_c]; /* * move the start of the encryption table * to the new location ptr */ curra = curra+asval_c; /* cycle around if getting close to the end of the string */ if (curra > tstbuf+95) { curra = curra - 95; } /* * For keeping track of the current location * you can ignore this for this example. */ curoffset = curoffset + asval_c; if (curoffset > 95) { curoffset = curoffset-95; } } } return(0); } /*----------------------------------------------------------* * * nb_decrypt *----------------------------------------------------------*/ int nb_decrypt (char *instr, char *outstr) { char *asval_a; char *asval_b; int asval_c; int tmpval; int tmpval1; asval_a = instr; if (myedata != 1) { /* ascii_filter+0x78 */ if (myedata != 0) { /* ascii_filter+0x94 */ incr_currc(); } myedata = 1; } outstr[0] = chptr[0]; incr_currc(); strcpy(outstr, instr+1); for (asval_b = outstr; *asval_b != '\0'; asval_b++) { if (*asval_b >= ' ') { /* Determine offset index */ tmpval = *asval_b & 0xff; /* Decode table is basically inverse mapping of encode table */ tmpval1 = (int) (decodetable[tmpval]&0xff) - curoffset; if (tmpval1 <0) { tmpval1 += 95; } *asval_b = tmpval1 + 32; asval_c = tmpval1; curoffset = curoffset + asval_c; if (curoffset > 95) { curoffset = curoffset-95; } } } return(0); } /*----------------------------------------------------------* * * incr_currc *----------------------------------------------------------*/ int incr_currc(void) { chptr++; if (chptr >= cstring+46) { chptr = cstring; } return(0); } One run of this program. myhost [6]% ./a.out orig : CThe quick brown fox jumps over the lazy dog. 1234567890 encstr: C(ln##v_lLaayw"*gg%yCC/r9@::4w\NN|f,,Ia@XXZT?uulyc9uwSMI. decstr: CThe quick brown fox jumps over the lazy dog. 1234567890 0.0u 0.0s 0:00 3%