home *** CD-ROM | disk | FTP | other *** search
- .oO Phrack 50 Oo.
-
- Volume Seven, Issue Fifty
-
- 13 of 16
-
- ===============================
- DTMF Encoding and Decoding In C
- by Mr. Blue
- ===============================
-
-
- Introduction
- ------------
- DTMF tones are the sounds emitted when you dial a number on your touch
- tone phone. Modems have traditionally been the device used to generate
- these tones from a computer. But the more sophisticated modems on the
- market today are nothing more than a DSP (digital signal processor) with
- accompanying built-in software to generate and interpet analog sounds into
- digital data. The computers sitting on your desk have more cpu power,
- a more complex OS, and very often a just as sophisticated DSP. There is
- no reason you can not duplicate the functionality of a modem from right
- inside of unix software, providing you with a lot easier to understand and
- modify code.
-
- In this article I provide the source code to both encode and decode
- DTMF tones. There are numerous uses for this code, for use in unix based
- phone scanning and war dialing programs, voice mail software, automated
- pbx brute force hacking, and countless other legitimate and not so
- legitimate uses.
-
- I will not go into depth explaining the underlying mathematical
- theories behind this code. If you are of a sufficient math background I
- would encourage you to research and learn about the algorithms used from
- your local college library; it is not my intent to summarize these
- algorithms, only to provide unix C code that can be used on its own or
- expanded to be used as part of a larger program.
-
- Use the extract utility included with Phrack to save the individual
- source files out to the dtmf/ directory. If you find this code useful, I
- would encourage you to show your appreciation by sharing some of your own
- knowledge with Phrack.
-
- <++> dtmf/detect.h
- /*
- *
- * goertzel aglorithm, find the power of different
- * frequencies in an N point DFT.
- *
- * ftone/fsample = k/N
- * k and N are integers. fsample is 8000 (8khz)
- * this means the *maximum* frequency resolution
- * is fsample/N (each step in k corresponds to a
- * step of fsample/N hz in ftone)
- *
- * N was chosen to minimize the sum of the K errors for
- * all the tones detected... here are the results :
- *
- * Best N is 240, with the sum of all errors = 3.030002
- * freq freq actual k kactual kerr
- * ---- ------------ ------ ------- -----
- * 350 (366.66667) 10.500 (11) 0.500
- * 440 (433.33333) 13.200 (13) 0.200
- * 480 (466.66667) 14.400 (14) 0.400
- * 620 (633.33333) 18.600 (19) 0.400
- * 697 (700.00000) 20.910 (21) 0.090
- * 700 (700.00000) 21.000 (21) 0.000
- * 770 (766.66667) 23.100 (23) 0.100
- * 852 (866.66667) 25.560 (26) 0.440
- * 900 (900.00000) 27.000 (27) 0.000
- * 941 (933.33333) 28.230 (28) 0.230
- * 1100 (1100.00000) 33.000 (33) 0.000
- * 1209 (1200.00000) 36.270 (36) 0.270
- * 1300 (1300.00000) 39.000 (39) 0.000
- * 1336 (1333.33333) 40.080 (40) 0.080
- **** I took out 1477.. too close to 1500
- * 1477 (1466.66667) 44.310 (44) 0.310
- ****
- * 1500 (1500.00000) 45.000 (45) 0.000
- * 1633 (1633.33333) 48.990 (49) 0.010
- * 1700 (1700.00000) 51.000 (51) 0.000
- * 2400 (2400.00000) 72.000 (72) 0.000
- * 2600 (2600.00000) 78.000 (78) 0.000
- *
- * notice, 697 and 700hz are indestinguishable (same K)
- * all other tones have a seperate k value.
- * these two tones must be treated as identical for our
- * analysis.
- *
- * The worst tones to detect are 350 (error = 0.5,
- * detet 367 hz) and 852 (error = 0.44, detect 867hz).
- * all others are very close.
- *
- */
-
- #define FSAMPLE 8000
- #define N 240
-
- int k[] = { 11, 13, 14, 19, 21, 23, 26, 27, 28, 33, 36, 39, 40,
- /*44,*/ 45, 49, 51, 72, 78, };
-
- /* coefficients for above k's as:
- * 2 * cos( 2*pi* k/N )
- */
- float coef[] = {
- 1.917639, 1.885283, 1.867161, 1.757634,
- 1.705280, 1.648252, 1.554292, 1.520812, 1.486290,
- 1.298896, 1.175571, 1.044997, 1.000000, /* 0.813473,*/
- 0.765367, 0.568031, 0.466891, -0.618034, -0.907981, };
-
- #define X1 0 /* 350 dialtone */
- #define X2 1 /* 440 ring, dialtone */
- #define X3 2 /* 480 ring, busy */
- #define X4 3 /* 620 busy */
-
- #define R1 4 /* 697, dtmf row 1 */
- #define R2 5 /* 770, dtmf row 2 */
- #define R3 6 /* 852, dtmf row 3 */
- #define R4 8 /* 941, dtmf row 4 */
- #define C1 10 /* 1209, dtmf col 1 */
- #define C2 12 /* 1336, dtmf col 2 */
- #define C3 13 /* 1477, dtmf col 3 */
- #define C4 14 /* 1633, dtmf col 4 */
-
- #define B1 4 /* 700, blue box 1 */
- #define B2 7 /* 900, bb 2 */
- #define B3 9 /* 1100, bb 3 */
- #define B4 11 /* 1300, bb4 */
- #define B5 13 /* 1500, bb5 */
- #define B6 15 /* 1700, bb6 */
- #define B7 16 /* 2400, bb7 */
- #define B8 17 /* 2600, bb8 */
-
- #define NUMTONES 18
-
- /* values returned by detect
- * 0-9 DTMF 0 through 9 or MF 0-9
- * 10-11 DTMF *, #
- * 12-15 DTMF A,B,C,D
- * 16-20 MF last column: C11, C12, KP1, KP2, ST
- * 21 2400
- * 22 2600
- * 23 2400 + 2600
- * 24 DIALTONE
- * 25 RING
- * 26 BUSY
- * 27 silence
- * -1 invalid
- */
- #define D0 0
- #define D1 1
- #define D2 2
- #define D3 3
- #define D4 4
- #define D5 5
- #define D6 6
- #define D7 7
- #define D8 8
- #define D9 9
- #define DSTAR 10
- #define DPND 11
- #define DA 12
- #define DB 13
- #define DC 14
- #define DD 15
- #define DC11 16
- #define DC12 17
- #define DKP1 18
- #define DKP2 19
- #define DST 20
- #define D24 21
- #define D26 22
- #define D2426 23
- #define DDT 24
- #define DRING 25
- #define DBUSY 26
- #define DSIL 27
-
- /* translation of above codes into text */
- char *dtran[] = {
- "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
- "*", "#", "A", "B", "C", "D",
- "+C11 ", "+C12 ", " KP1+", " KP2+", "+ST ",
- " 2400 ", " 2600 ", " 2400+2600 ",
- " DIALTONE ", " RING ", " BUSY ","" };
-
- #define RANGE 0.1 /* any thing higher than RANGE*peak is "on" */
- #define THRESH 100.0 /* minimum level for the loudest tone */
- #define FLUSH_TIME 100 /* 100 frames = 3 seconds */
-
- <-->
- <++> dtmf/detect.c
-
- /*
- * detect.c
- * This program will detect MF tones and normal
- * dtmf tones as well as some other common tones such
- * as BUSY, DIALTONE and RING.
- * The program uses a goertzel algorithm to detect
- * the power of various frequency ranges.
- *
- * input is assumed to be 8 bit samples. The program
- * can use either signed or unsigned samples according
- * to a compile time option:
- *
- * cc -DUNSIGNED detect.c -o detect
- *
- * for unsigned input (soundblaster) and:
- *
- * cc detect.c -o detect
- *
- * for signed input (amiga samples)
- * if you dont want flushes, -DNOFLUSH
- *
- * Tim N.
- */
-
- #include <stdio.h>
- #include <math.h>
- #include "detect.h"
-
- /*
- * calculate the power of each tone according
- * to a modified goertzel algorithm described in
- * _digital signal processing applications using the
- * ADSP-2100 family_ by Analog Devices
- *
- * input is 'data', N sample values
- *
- * ouput is 'power', NUMTONES values
- * corresponding to the power of each tone
- */
- calc_power(data,power)
- #ifdef UNSIGNED
- unsigned char *data;
- #else
- char *data;
- #endif
- float *power;
- {
- float u0[NUMTONES],u1[NUMTONES],t,in;
- int i,j;
-
- for(j=0; j<NUMTONES; j++) {
- u0[j] = 0.0;
- u1[j] = 0.0;
- }
- for(i=0; i<N; i++) { /* feedback */
- #ifdef UNSIGNED
- in = ((int)data[i] - 128) / 128.0;
- #else
- in = data[i] / 128.0;
- #endif
- for(j=0; j<NUMTONES; j++) {
- t = u0[j];
- u0[j] = in + coef[j] * u0[j] - u1[j];
- u1[j] = t;
- }
- }
- for(j=0; j<NUMTONES; j++) /* feedforward */
- power[j] = u0[j] * u0[j] + u1[j] * u1[j] - coef[j] * u0[j] * u1[j];
- return(0);
- }
-
-
- /*
- * detect which signals are present.
- *
- * return values defined in the include file
- * note: DTMF 3 and MF 7 conflict. To resolve
- * this the program only reports MF 7 between
- * a KP and an ST, otherwise DTMF 3 is returned
- */
- decode(data)
- char *data;
- {
- float power[NUMTONES],thresh,maxpower;
- int on[NUMTONES],on_count;
- int bcount, rcount, ccount;
- int row, col, b1, b2, i;
- int r[4],c[4],b[8];
- static int MFmode=0;
-
- calc_power(data,power);
- for(i=0, maxpower=0.0; i<NUMTONES;i++)
- if(power[i] > maxpower)
- maxpower = power[i];
- /*
- for(i=0;i<NUMTONES;i++)
- printf("%f, ",power[i]);
- printf("\n");
- */
-
- if(maxpower < THRESH) /* silence? */
- return(DSIL);
- thresh = RANGE * maxpower; /* allowable range of powers */
- for(i=0, on_count=0; i<NUMTONES; i++) {
- if(power[i] > thresh) {
- on[i] = 1;
- on_count ++;
- } else
- on[i] = 0;
- }
-
- /*
- printf("%4d: ",on_count);
- for(i=0;i<NUMTONES;i++)
- putchar('0' + on[i]);
- printf("\n");
- */
-
- if(on_count == 1) {
- if(on[B7])
- return(D24);
- if(on[B8])
- return(D26);
- return(-1);
- }
-
- if(on_count == 2) {
- if(on[X1] && on[X2])
- return(DDT);
- if(on[X2] && on[X3])
- return(DRING);
- if(on[X3] && on[X4])
- return(DBUSY);
-
- b[0]= on[B1]; b[1]= on[B2]; b[2]= on[B3]; b[3]= on[B4];
- b[4]= on[B5]; b[5]= on[B6]; b[6]= on[B7]; b[7]= on[B8];
- c[0]= on[C1]; c[1]= on[C2]; c[2]= on[C3]; c[3]= on[C4];
- r[0]= on[R1]; r[1]= on[R2]; r[2]= on[R3]; r[3]= on[R4];
-
- for(i=0, bcount=0; i<8; i++) {
- if(b[i]) {
- bcount++;
- b2 = b1;
- b1 = i;
- }
- }
- for(i=0, rcount=0; i<4; i++) {
- if(r[i]) {
- rcount++;
- row = i;
- }
- }
- for(i=0, ccount=0; i<4; i++) {
- if(c[i]) {
- ccount++;
- col = i;
- }
- }
-
- if(rcount==1 && ccount==1) { /* DTMF */
- if(col == 3) /* A,B,C,D */
- return(DA + row);
- else {
- if(row == 3 && col == 0 )
- return(DSTAR);
- if(row == 3 && col == 2 )
- return(DPND);
- if(row == 3)
- return(D0);
- if(row == 0 && col == 2) { /* DTMF 3 conflicts with MF 7 */
- if(!MFmode)
- return(D3);
- } else
- return(D1 + col + row*3);
- }
- }
-
- if(bcount == 2) { /* MF */
- /* b1 has upper number, b2 has lower */
- switch(b1) {
- case 7: return( (b2==6)? D2426: -1);
- case 6: return(-1);
- case 5: if(b2==2 || b2==3) /* KP */
- MFmode=1;
- if(b2==4) /* ST */
- MFmode=0;
- return(DC11 + b2);
- /* MF 7 conflicts with DTMF 3, but if we made it
- * here then DTMF 3 was already tested for
- */
- case 4: return( (b2==3)? D0: D7 + b2);
- case 3: return(D4 + b2);
- case 2: return(D2 + b2);
- case 1: return(D1);
- }
- }
- return(-1);
- }
-
- if(on_count == 0)
- return(DSIL);
- return(-1);
- }
-
- read_frame(fd,buf)
- int fd;
- char *buf;
- {
- int i,x;
-
- for(i=0; i<N; ) {
- x = read(fd, &buf[i], N-i);
- if(x <= 0)
- return(0);
- i += x;
- }
- return(1);
- }
-
- /*
- * read in frames, output the decoded
- * results
- */
- dtmf_to_ascii(fd1, fd2)
- int fd1;
- FILE *fd2;
- {
- int x,last= DSIL;
- char frame[N+5];
- int silence_time;
-
- while(read_frame(fd1, frame)) {
- x = decode(frame);
- /*
- if(x== -1) putchar('-');
- if(x==DSIL) putchar(' ');
- if(x!=DSIL && x!=-1) putchar('a' + x);
- fflush(stdout);
- continue;
- */
-
- if(x >= 0) {
- if(x == DSIL)
- silence_time += (silence_time>=0)?1:0 ;
- else
- silence_time= 0;
- if(silence_time == FLUSH_TIME) {
- fputs("\n",fd2);
- silence_time= -1; /* stop counting */
- }
-
- if(x != DSIL && x != last &&
- (last == DSIL || last==D24 || last == D26 ||
- last == D2426 || last == DDT || last == DBUSY ||
- last == DRING) ) {
- fputs(dtran[x], fd2);
- #ifndef NOFLUSH
- fflush(fd2);
- #endif
- }
- last = x;
- }
- }
- fputs("\n",fd2);
- }
-
- main(argc,argv)
- int argc;
- char **argv;
- {
- FILE *output;
- int input;
-
- input = 0;
- output = stdout;
- switch(argc) {
- case 1: break;
- case 3: output = fopen(argv[2],"w");
- if(!output) {
- perror(argv[2]);
- return(-1);
- }
- /* fall through */
- case 2: input = open(argv[1],0);
- if(input < 0) {
- perror(argv[1]);
- return(-1);
- }
- break;
- default:
- fprintf(stderr,"usage: %s [input [output]]\n",argv[0]);
- return(-1);
- }
- dtmf_to_ascii(input,output);
- fputs("Done.\n",output);
- return(0);
- }
-
- <-->
- <++> dtmf/gen.c
-
- /* -------- local defines (if we had more.. seperate file) ----- */
- #define FSAMPLE 8000 /* sampling rate, 8KHz */
-
- /*
- * FLOAT_TO_SAMPLE converts a float in the range -1.0 to 1.0
- * into a format valid to be written out in a sound file
- * or to a sound device
- */
- #ifdef SIGNED
- # define FLOAT_TO_SAMPLE(x) ((char)((x) * 127.0))
- #else
- # define FLOAT_TO_SAMPLE(x) ((char)((x + 1.0) * 127.0))
- #endif
-
- #define SOUND_DEV "/dev/dsp"
- typedef char sample;
- /* --------------------------------------------------------------- */
-
- #include <fcntl.h>
-
- /*
- * take the sine of x, where x is 0 to 65535 (for 0 to 360 degrees)
- */
- float mysine(in)
- short in;
- {
- static coef[] = {
- 3.140625, 0.02026367, -5.325196, 0.5446778, 1.800293 };
- float x,y,res;
- int sign,i;
-
- if(in < 0) { /* force positive */
- sign = -1;
- in = -in;
- } else
- sign = 1;
- if(in >= 0x4000) /* 90 degrees */
- in = 0x8000 - in; /* 180 degrees - in */
- x = in * (1/32768.0);
- y = x; /* y holds x^i) */
- res = 0;
- for(i=0; i<5; i++) {
- res += y * coef[i];
- y *= x;
- }
- return(res * sign);
- }
-
- /*
- * play tone1 and tone2 (in Hz)
- * for 'length' milliseconds
- * outputs samples to sound_out
- */
- two_tones(sound_out,tone1,tone2,length)
- int sound_out;
- unsigned int tone1,tone2,length;
- {
- #define BLEN 128
- sample cout[BLEN];
- float out;
- unsigned int ad1,ad2;
- short c1,c2;
- int i,l,x;
-
- ad1 = (tone1 << 16) / FSAMPLE;
- ad2 = (tone2 << 16) / FSAMPLE;
- l = (length * FSAMPLE) / 1000;
- x = 0;
- for( c1=0, c2=0, i=0 ;
- i < l;
- i++, c1+= ad1, c2+= ad2 ) {
- out = (mysine(c1) + mysine(c2)) * 0.5;
- cout[x++] = FLOAT_TO_SAMPLE(out);
- if (x==BLEN) {
- write(sound_out, cout, x * sizeof(sample));
- x=0;
- }
- }
- write(sound_out, cout, x);
- }
-
- /*
- * silence on 'sound_out'
- * for length milliseconds
- */
- silence(sound_out,length)
- int sound_out;
- unsigned int length;
- {
- int l,i,x;
- static sample c0 = FLOAT_TO_SAMPLE(0.0);
- sample cout[BLEN];
-
- x = 0;
- l = (length * FSAMPLE) / 1000;
- for(i=0; i < l; i++) {
- cout[x++] = c0;
- if (x==BLEN) {
- write(sound_out, cout, x * sizeof(sample));
- x=0;
- }
- }
- write(sound_out, cout, x);
- }
-
- /*
- * play a single dtmf tone
- * for a length of time,
- * input is 0-9 for digit, 10 for * 11 for #
- */
- dtmf(sound_fd, digit, length)
- int sound_fd;
- int digit, length;
- {
- /* Freqs for 0-9, *, # */
- static int row[] = {
- 941, 697, 697, 697, 770, 770, 770, 852, 852, 852, 941, 941 };
- static int col[] = {
- 1336, 1209, 1336, 1477, 1209, 1336, 1477, 1209, 1336, 1447,
- 1209, 1477 };
-
- two_tones(sound_fd, row[digit], col[digit], length);
- }
-
- /*
- * take a string and output as dtmf
- * valid characters, 0-9, *, #
- * all others play as 50ms silence
- */
- dial(sound_fd, number)
- int sound_fd;
- char *number;
- {
- int i,x;
- char c;
-
- for(i=0;number[i];i++) {
- c = number[i];
- x = -1;
- if(c >= '0' && c <= '9')
- x = c - '0';
- else if(c == '*')
- x = 10;
- else if(c == '#')
- x = 11;
- if(x >= 0)
- dtmf(sound_fd, x, 50);
- silence(sound_fd,50);
- }
- }
-
- main()
- {
- int sfd;
- char number[100];
-
- sfd = open(SOUND_DEV,O_RDWR);
- if(sfd<0) {
- perror(SOUND_DEV);
- return(-1);
- }
- printf("Enter fone number: ");
- gets(number);
- dial(sfd,number);
- }
- <-->
- <++> dtmf/Makefile
- #
- # Defines:
- # UNSIGNED - use unsigned 8 bit samples
- # otherwise use signed 8 bit samples
- #
-
- CFLAGS= -DUNSIGNED
-
- default: detect gen
-
- detect: detect.c
- $(CC) detect.c -o detect
-
- gen: gen.c
- $(CC) gen.c -o gen
-
- clobber: clean
- rm -rf detect gen
-
- clean:
- rm -rf *.o core a.out
- <-->
-
- EOF
-