|
@@ -0,0 +1,391 @@
|
|
|
+#!/usr/bin/php
|
|
|
+<?php
|
|
|
+
|
|
|
+/*
|
|
|
+ This program is free software: you can redistribute it and/or modify
|
|
|
+ it under the terms of the GNU General Public License as published by
|
|
|
+ the Free Software Foundation, either version 3 of the License, or
|
|
|
+ (at your option) any later version.
|
|
|
+
|
|
|
+ This program is distributed in the hope that it will be useful,
|
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+ GNU General Public License for more details.
|
|
|
+
|
|
|
+ You should have received a copy of the GNU General Public License
|
|
|
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
+*/
|
|
|
+
|
|
|
+const SNAME='decrypt';
|
|
|
+const SVERS='0.1';
|
|
|
+
|
|
|
+$conf=[
|
|
|
+ 'phrase'=>null,
|
|
|
+ 'encrypt'=>false,
|
|
|
+ 'method'=>0,
|
|
|
+ 'write'=>false,
|
|
|
+ 'norm'=>false,
|
|
|
+ 'progress'=>false,
|
|
|
+ 'substabfp'=>'substable.txt',
|
|
|
+ 'solsfp'=>'solutions.txt',
|
|
|
+ 'dictfps'=>[],
|
|
|
+ 'debug'=>false
|
|
|
+];
|
|
|
+
|
|
|
+$help=
|
|
|
+'SYNOPSIS
|
|
|
+
|
|
|
+'.SNAME.' -d <dictionary file> <phrase>
|
|
|
+
|
|
|
+DESCRIPTION
|
|
|
+
|
|
|
+This is '.SNAME.' v'.SVERS.', a CLI PHP script that can help decrypting
|
|
|
+phrases encrypted with any algorithm producing an encrypted phrase where each
|
|
|
+symbol always replaces the same, different symbol from the clear text one.
|
|
|
+
|
|
|
+COMMAND LINE OPTIONS
|
|
|
+
|
|
|
+-d, --dictfp <dictionary file>
|
|
|
+ Load a dictionary file. It has to be a simple text file with one word on each
|
|
|
+ line. You have to specify at least one, and you can specify more than one
|
|
|
+ prepending «-d» or «--dictfp» to each file path.
|
|
|
+ Currently '.SNAME.' comes with two xz compressed dictionary files:
|
|
|
+ «english.txt.xz» and «italiano.txt.xz». To use any of them, you have to
|
|
|
+ decompress it with the xz program. They both are dumps from the corresponding
|
|
|
+ aspell dictionaries, with each lemma declined in all its forms. You can
|
|
|
+ get dumps for other languages with something like this:
|
|
|
+ aspell -d <lang code> dump master | aspell -l <lang code> expand 4
|
|
|
+ (You may need to further process the above command output with tr, grep, sed
|
|
|
+ and the likes, in order to have a functional dictionary file).
|
|
|
+-n, --normalize
|
|
|
+ “Normalize” each dictionar(y|ies) entry replacing every accented or otherwise
|
|
|
+ “signed” character with its “plain” form, and removing possible duplicated
|
|
|
+ entries. For example, this will substitute every “è” character with “e”.
|
|
|
+-e, --encrypt
|
|
|
+ Encrypt the given phrase. Useful for testing purposes. By default, or when
|
|
|
+ «-m» or «--method» is set to «0», it encrypts the phrase by simply switching
|
|
|
+ every source character with its subsequent character in the UTF-8 table. With
|
|
|
+ «-m» or «--method» set to «1», it encrypts the phrase using a random
|
|
|
+ substition table.
|
|
|
+-m, --method <0|1>
|
|
|
+ Choose a method for encryption when «-e» or «--encrypt» is specified (see
|
|
|
+ previous option description). It defaults to “'.$conf['method'].'”.
|
|
|
+-p, --progress
|
|
|
+ Show progress during script execution.
|
|
|
+-w, --write
|
|
|
+ Write any substition table '.SNAME.' may find into a «'.$conf['substabfp'].'»
|
|
|
+ file and any solution '.SNAME.' may find into a «'.$conf['solsfp'].'» file.
|
|
|
+ Both files will be written in current working directory. Note that possible
|
|
|
+ existing files with the same name will be overwritten.
|
|
|
+-h, --help
|
|
|
+ Show this help text and exit.
|
|
|
+
|
|
|
+EXAMPLE
|
|
|
+
|
|
|
+decrypt "rûéï eïw6ï w 6fwh6ï" -d english.txt -p -w
|
|
|
+
|
|
|
+Will produce 983 solutions, with «give peace a chance» being one of them,
|
|
|
+showing progress during the execution, writing substitution tables into
|
|
|
+«'.$conf['substabfp'].'» and solutions into «'.$conf['solsfp'].'» within current working
|
|
|
+directory.
|
|
|
+
|
|
|
+DISCLAIMER AND LICENSE
|
|
|
+
|
|
|
+This program comes with ABSOLUTELY NO WARRANTY; for details see the source.
|
|
|
+This is free software, and you are welcome to redistribute it under certain
|
|
|
+conditions; see <http://www.gnu.org/licenses/> for details.';
|
|
|
+
|
|
|
+// 4E E1V VXVTHM VP SRR 0AY DA23I5VO L 2W4T WL 9ONA1Z K5 HIDUFJ 88LC NZD4 S50B.
|
|
|
+
|
|
|
+// sempre caro mi fu quest'ermo colle
|
|
|
+// tfnqsf dbsp nj gv rvftu'fsnp dpmmf
|
|
|
+
|
|
|
+// e il naufragar m'è dolce in questo mare
|
|
|
+// f jm obvgsbhbs n'é epmdf jo rvftup nbsf
|
|
|
+// 6 v0 7òôõíò3òí x'à êú0ø6 v7 ûô6hwú xòí6 (casuale)
|
|
|
+
|
|
|
+for ($i=1; $i<$argc; $i++) {
|
|
|
+ if ($argv[$i]=='-h' || $argv[$i]=='--help') {
|
|
|
+ echo "{$help}\n";
|
|
|
+ exit(0);
|
|
|
+ } elseif ($argv[$i]=='--makereadme') {
|
|
|
+ file_put_contents(__DIR__.'/README.md',"```text\n{$help}\n```\n");
|
|
|
+ exit(0);
|
|
|
+ } elseif ($argv[$i]=='-e' || $argv[$i]=='--encrypt') {
|
|
|
+ $conf['encrypt']=true;
|
|
|
+ } elseif ($argv[$i]=='-d' || $argv[$i]=='--dictfp') {
|
|
|
+ if ($i<$argc-1) {
|
|
|
+ $i++;
|
|
|
+ $conf['dictfps'][]=$argv[$i];
|
|
|
+ } else {
|
|
|
+ edie("«{$argv[$i]}» requires an argument (use «-h» or «--help» for help); shutting down.\n",1);
|
|
|
+ }
|
|
|
+ } elseif ($argv[$i]=='-m' || $argv[$i]=='--method') {
|
|
|
+ if ($i<$argc-1) {
|
|
|
+ if (in_array($argv[$i+1],['0','1']))
|
|
|
+ $conf['method']=$argv[$i+1]+0;
|
|
|
+ else
|
|
|
+ edie("«{$argv[$i+1]}» is not a valid argument for «{$argv[$i]}» (use «-h» or «--help» for help); shutting down.\n",1);
|
|
|
+ $i++;
|
|
|
+ } else {
|
|
|
+ edie("«{$argv[$i]}» requires an argument (use «-h» or «--help» for help); shutting down.\n",1);
|
|
|
+ }
|
|
|
+ } elseif ($argv[$i]=='-w' || $argv[$i]=='--write') {
|
|
|
+ $conf['write']=true;
|
|
|
+ } elseif ($argv[$i]=='-n' || $argv[$i]=='--norm') {
|
|
|
+ $conf['norm']=true;
|
|
|
+ } elseif ($argv[$i]=='-p' || $argv[$i]=='--progress') {
|
|
|
+ $conf['progress']=true;
|
|
|
+ } elseif (is_null($conf['phrase'])) {
|
|
|
+ $conf['phrase']=trim($argv[$i]);
|
|
|
+ } else {
|
|
|
+ edie("a phrase has already been specified and «{$argv[$i]}» is not a known option (use «-h» or «--help» for help); shutting down.\n",1);
|
|
|
+ }
|
|
|
+}
|
|
|
+//print_r($conf); pause();
|
|
|
+
|
|
|
+if (is_null($conf['phrase']))
|
|
|
+ edie("you have not specified a phrase (use «-h» or «--help» for help); shutting down.\n",1);
|
|
|
+elseif ($conf['phrase']=='')
|
|
|
+ edie("you can’t specify an empty string as phrase (use «-h» or «--help» for help); shutting down.\n",1);
|
|
|
+
|
|
|
+if ($conf['encrypt']) {
|
|
|
+ $phrase=mb_strtolower($conf['phrase']);
|
|
|
+ if ($conf['method']==1) {
|
|
|
+ $ichars='aàáâãäåbcdeèéêëfghiìíîïjklmnoòóôõöøpqrstuùúûüvwxyz0123456789';
|
|
|
+ $len=mb_strlen($ichars);
|
|
|
+ for ($i=0; $i<$len; $i++)
|
|
|
+ $buff[]=mb_substr($ichars,$i,1);
|
|
|
+ $ichars=$buff;
|
|
|
+ shuffle($buff);
|
|
|
+ for ($i=0; $i<$len; $i++)
|
|
|
+ $ochars[$ichars[$i]]=$buff[$i];
|
|
|
+ $len=mb_strlen($phrase);
|
|
|
+ $buff='';
|
|
|
+ for ($i=0; $i<$len; $i++) {
|
|
|
+ $c=mb_substr($phrase,$i,1);
|
|
|
+ if (array_key_exists($c,$ochars))
|
|
|
+ $buff.=$ochars[$c];
|
|
|
+ else
|
|
|
+ $buff.=$c;
|
|
|
+
|
|
|
+ }
|
|
|
+ } elseif ($conf['method']==0) {
|
|
|
+ $buff='';
|
|
|
+ for ($i=0; $i<mb_strlen($phrase); $i++) {
|
|
|
+ $c=mb_substr($phrase,$i,1);
|
|
|
+ if ($c==' ')
|
|
|
+ $buff.=' ';
|
|
|
+ else
|
|
|
+ $buff.=mb_chr(mb_ord($c)+1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ echo "{$buff}\n";
|
|
|
+ exit(0);
|
|
|
+}
|
|
|
+
|
|
|
+if (count($conf['dictfps'])==0)
|
|
|
+ edie("you have specified no dictionaries (use «-h» or «--help» for help); shutting down.\n",1);
|
|
|
+$norma=[
|
|
|
+ 'a'=>['à','á','â','ã','ä','å'],
|
|
|
+ 'e'=>['è','é','ê','ë'],
|
|
|
+ 'i'=>['ì','í','î','ï'],
|
|
|
+ 'o'=>['ò','ó','ô','õ','ö','ø'],
|
|
|
+ 'u'=>['ù','ú','û','ü']
|
|
|
+];
|
|
|
+$dict=[];
|
|
|
+foreach ($conf['dictfps'] as $fp) {
|
|
|
+ $buff=@file($fp,FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES);
|
|
|
+ if ($buff===false)
|
|
|
+ edie("could not open dictionary file «{$fp}» (use «-h» or «--help» for help); shutting down.\n",1);
|
|
|
+ foreach ($buff as $k=>$v)
|
|
|
+ $conf['norm'] ? $dict[]=norm(mb_strtolower($v)) : $dict[]=mb_strtolower($v);
|
|
|
+}
|
|
|
+$dict=array_unique($dict);
|
|
|
+//shuffle($dict);
|
|
|
+foreach ($dict as $k=>$v) {
|
|
|
+ $len=mb_strlen($v);
|
|
|
+ $buff=[];
|
|
|
+ for ($i=0; $i<$len; $i++) {
|
|
|
+ $dc=mb_substr($v,$i,1);
|
|
|
+ if ($conf['method']==1)
|
|
|
+ $dc=mb_chr(mb_ord($dc)+$i);
|
|
|
+ $buff[]=$dc;
|
|
|
+ }
|
|
|
+ $dict[$k]=['len'=>$len,'splitword'=>$buff,'word'=>$v];
|
|
|
+}
|
|
|
+if (count($dict)==0)
|
|
|
+ edie("the dictionar(y|ies) you have specified contain(s) no words; shutting down.\n",1);
|
|
|
+//foreach ($dict as $k=>$v) echo "{$v['word']}\n"; die();
|
|
|
+
|
|
|
+$phrase=preg_replace('#\s+#u',' ',$conf['phrase']);
|
|
|
+$phrase=mb_strtolower($phrase);
|
|
|
+$cphrase=$phrase;
|
|
|
+$phrase=preg_split('#\W+#u',$phrase);
|
|
|
+//$phrase=explode(' ',$phrase);
|
|
|
+$buff=[];
|
|
|
+foreach ($phrase as $k=>$v)
|
|
|
+ $buff[$k]=mb_strlen($v);
|
|
|
+arsort($buff,SORT_NUMERIC);
|
|
|
+foreach ($phrase as $k=>$v) {
|
|
|
+ $len=mb_strlen($v);
|
|
|
+ $split=[];
|
|
|
+ for ($i=0; $i<$len; $i++)
|
|
|
+ $split[]=mb_substr($v,$i,1);
|
|
|
+ $buff[$k]=['len'=>$len,'splitword'=>$split,'word'=>$v,'sols'=>[]];
|
|
|
+}
|
|
|
+$phrase=$buff;
|
|
|
+//print_r($phrase); pause();
|
|
|
+
|
|
|
+$c=count($phrase);
|
|
|
+$d=0;
|
|
|
+$cns=0;
|
|
|
+$substa=[[]];
|
|
|
+foreach ($phrase as $pindex=>$pword) {
|
|
|
+ $d++;
|
|
|
+ $ib=$pindex+1;
|
|
|
+ eprog("Working on phrase word {$ib}: «{$pword['word']}» ({$d}/{$c})\n");
|
|
|
+ $newsubsta=ckdict($dict,$pword,$pindex,$substa);
|
|
|
+ /*foreach ($newsubsta as $k=>$v)
|
|
|
+ echo strsubsta($v)."\n";*/
|
|
|
+ $cns=count($newsubsta);
|
|
|
+ eprog("Finished working on phrase word {$ib}: «{$pword['word']}» ({$d}/{$c}); found {$cns} compatible keys.\n");
|
|
|
+ //pause();
|
|
|
+ if ($cns==0) break;
|
|
|
+ $substa=$newsubsta;
|
|
|
+ unset($newsubsta);
|
|
|
+ /*if ($conf['progress']) {
|
|
|
+ foreach ($substa as $subst) {
|
|
|
+ $out='';
|
|
|
+ foreach ($xphrase as $pword) {
|
|
|
+ $out.=getsubst($subst,$pword['word']).' ';
|
|
|
+ }
|
|
|
+ $out=substr($out,0,-1);
|
|
|
+ //echo strsubsta($subst)."\n";
|
|
|
+ echo "{$out}\n";
|
|
|
+ }
|
|
|
+ echo "---\n";
|
|
|
+ pause();
|
|
|
+ }*/
|
|
|
+}
|
|
|
+if (count($substa)>0) {
|
|
|
+ if ($conf['write'])
|
|
|
+ if (false===file_put_contents($conf['substabfp'],print_r($substa,true)))
|
|
|
+ eerr("Warning: could not open «{$conf['substabfp']}» for writing.\n");
|
|
|
+ //ksort($phrase,SORT_NUMERIC);
|
|
|
+ $i=0;
|
|
|
+ if ($conf['write']) {
|
|
|
+ $f=@fopen($conf['solsfp'],'w');
|
|
|
+ if ($f===false)
|
|
|
+ eerr("Warning: could not open «{$conf['solsfp']}» for writing.\n");
|
|
|
+ }
|
|
|
+ foreach ($substa as $subst) {
|
|
|
+ $i++;
|
|
|
+ $oline=$i.': '.getsubst($subst,$cphrase)."\n";
|
|
|
+ eprog($oline);
|
|
|
+ if ($conf['write'] && $f!==false) fwrite($f,$oline);
|
|
|
+ }
|
|
|
+ if ($conf['write'] && $f!==false) fclose($f);
|
|
|
+ if ($cns==0) echo "Warning: no complete solutions found.\n";
|
|
|
+} else {
|
|
|
+ echo "No solutions found.\n";
|
|
|
+}
|
|
|
+exit(0);
|
|
|
+
|
|
|
+function ckdict(&$dict,$pword,$pindex,$substa) {
|
|
|
+ global $phrase, $conf;
|
|
|
+ $newsubsta=[];
|
|
|
+ foreach ($dict as $dword) {
|
|
|
+ if ($dword['len']==$pword['len']) {
|
|
|
+ //echo "«{$pword['word']}» has same length as «{$dword['word']}»\n";
|
|
|
+ foreach ($substa as $subst) {
|
|
|
+ $newsubst=$subst;
|
|
|
+ //echo '-> '.strsubsta($newsubst)."\n";
|
|
|
+ $ok=true;
|
|
|
+ for ($i=0; $i<$pword['len']; $i++) {
|
|
|
+ $pc=$pword['splitword'][$i];
|
|
|
+ $dc=$dword['splitword'][$i];
|
|
|
+ //$dc=mb_chr(mb_ord($dword['splitword'][$i])+$i);
|
|
|
+ $x=array_search($dc,$newsubst,true);
|
|
|
+ if (($x!==false && $x!=$pc) || (array_key_exists($pc,$newsubst) && $newsubst[$pc]!=$dc)) {
|
|
|
+ $ok=false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ $newsubst[$pc]=$dc;
|
|
|
+ }
|
|
|
+ if ($ok) {
|
|
|
+ if ($conf['progress'] && !in_array($dword['word'],$phrase[$pindex]['sols'])) {
|
|
|
+ $phrase[$pindex]['sols'][]=$dword['word'];
|
|
|
+ echo "«{$pword['word']}» could be «{$dword['word']}»\n";
|
|
|
+ }
|
|
|
+ $newsubsta[]=$newsubst;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return $newsubsta;
|
|
|
+}
|
|
|
+
|
|
|
+function eprog($str) {
|
|
|
+ global $conf;
|
|
|
+ if ($conf['progress'])
|
|
|
+ echo $str;
|
|
|
+}
|
|
|
+
|
|
|
+function eerr($str) {
|
|
|
+ fwrite(STDERR,$str);
|
|
|
+}
|
|
|
+
|
|
|
+function pause() {
|
|
|
+ echo "Press return to continue ";
|
|
|
+ fgets(STDIN);
|
|
|
+}
|
|
|
+
|
|
|
+function getsubst($subst,$str) {
|
|
|
+ $out='';
|
|
|
+ $len=mb_strlen($str);
|
|
|
+ for ($i=0; $i<$len; $i++) {
|
|
|
+ $sc=mb_substr($str,$i,1);
|
|
|
+ //$sc=mb_chr(mb_ord(mb_substr($str,$i,1))+$i);
|
|
|
+ if (array_key_exists($sc,$subst))
|
|
|
+ $out.="\33[0;7m{$subst[$sc]}\33[0;0m";
|
|
|
+ else
|
|
|
+ $out.=$sc;
|
|
|
+ }
|
|
|
+ str_replace("\33[0;0m\33[0;7m",'',$out);
|
|
|
+ return $out;
|
|
|
+}
|
|
|
+
|
|
|
+function norm($str) {
|
|
|
+ global $norma;
|
|
|
+ $out='';
|
|
|
+ $len=mb_strlen($str);
|
|
|
+ for ($i=0; $i<$len; $i++) {
|
|
|
+ $sc=mb_substr($str,$i,1);
|
|
|
+ //echo "sc: {$sc}\n";
|
|
|
+ $nc=$sc;
|
|
|
+ foreach ($norma as $k=>$nnca)
|
|
|
+ if (in_array($sc,$nnca)) {
|
|
|
+ $nc=$k;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ $out.=$nc;
|
|
|
+ }
|
|
|
+ return $out;
|
|
|
+}
|
|
|
+
|
|
|
+function strsubsta($arr) {
|
|
|
+ $out='';
|
|
|
+ foreach ($arr as $key=>$val)
|
|
|
+ $out.="{$key}={$val}; ";
|
|
|
+ if (strlen($out)>0)
|
|
|
+ $out=substr($out,0,-2);
|
|
|
+ return $out;
|
|
|
+}
|
|
|
+
|
|
|
+function edie($msg,$ec) {
|
|
|
+ fwrite(STDERR,'Error: '.$msg);
|
|
|
+ exit($ec);
|
|
|
+}
|
|
|
+
|
|
|
+?>
|