Jump to content


  • Content count

  • Joined

  • Last visited

Community Reputation

4 Neutral

About Obfuscate

  • Rank
  1. I modified the wiki title filtering program to extract all n-grams. This increases the size of the candidate word list to 138 million, and finds a couple more reasonable preimages: 0x10B9683D PARM_SPECIAL #8 "ISITCLEAR" 0x72A47EF6 WWChat "DOTHISMYSELF " No luck with PARM_BIB or PARM_CHEATDAVID. It seems to be hard for any input to hash to these values. Even with the whole n-gram candidate list, only one preimage is found for each, neither very plausible: 0xF7867BF0 PARM_BIB "6C50-97F-9C02-EDD1." 0xBE79088C PARM_CHEATDAVID "EYETOO..." I suspect that one reason some hash outputs are more difficult to achieve than others is a bug in the "unroll" step of Obfuscate. The code looks like it is trying to shift the lower 8 bits of temp into the upper 8 bits of code. But the code>>8 is an arithmetic (signed) right shift, not a logical right shift. If the most significant bit of code is 1, the shift operation makes the upper 8 bits become 0xff, not 0x00 as expected. After a few more iterations code becomes 0xffffffff, which makes the second half of the buffer often be filled with 0xff bytes. The lack of variation in half of the buffer increases the probability of collisions in this intermediate step of the hash computation. I noticed that too. This code looks like it is trying to compute the "CRC"s of the forward and backward buffer and XOR them together: long code = Calculate_CRC(buffer, length); long copy = code; strrev(buffer); code ^= Calculate_CRC(buffer, length); code = code ^ copy; strrev(buffer); But the XOR operations cancel out and it becomes equivalent to the "CRC" of only the backward buffer: strrev(buffer); long code = Calculate_CRC(buffer, length); strrev(buffer); This can be optimized further (eliminating the two strrevs) with a specialized reverse-CRC function that byteswaps each 4-byte chunk and iterates over the chunks in reverse order. cnc_obfuscate_20231121.zip
  2. I'm glad you replied. I knew you had worked on reversing the hashes, but I did not know the extent of what you had already found. I also did not know about the hashes in Red Alert. After reading your explanation, I agree "OBWAN" is right for PARM_CHEATJOE. I don't have many more ideas, other than trying a dedicated password cracker, for example John the Ripper. It obviously doesn't have support for this custom hash algorithm, but you can run it in with --stdout and pipe it into another program (--stdout is desirable anyway, because it will keep finding additional inputs for the same hash). The below john.conf file implements variation rules that are basically equivalent to what I had in my custom program: [List.Rules:Wordlist] # the original word : # add various suffixes : $[.?!] # strip whitespace /?w @?w /?w @?w $[.?!] # replace whitespace with other characters /?w s?w[_\-] /?w s?w[_\-] $[.?!] # strip punctuation and symbols /?p @?p @?s /?p @?p @?s $[.?!] /?s @?p @?s /?s @?p @?s $[.?!] # add dash prefix if otherwise ok for command line /?w /?p /?s ^- [Incremental:ASCII] File = $JOHN/ascii.chr MinLen = 0 CharCount = 95 After hacking my earlier program not to apply its own variations, it can be run as follows: john --wordlist=wordlist.txt --rules --stdout | cargo run --release The incremental mode finds other preimages, not using a wordlist: john --incremental --stdout | cargo run --release I did not find any plausible-looking preimages for the remaining hashes this way, however. Any progress by this method will require a different wordlist and/or different variation rules.
  3. I also tried using the Z3 theorem prover to find inputs that hash to the desired codes, with limited success. It does find working preimages, but it's slow and the preimages it finds are obviously random hash collisions, not the inputs intended by the developers. The only one I saw that looked remotely plausible was "OBWAN" for 0xABDD0362 (PARM_CHEATJOE), but that's not as clearly correct as known cheat strings like "SUPER" and "WIZARD". I'm no Z3 expert, so maybe the program can be improved. The source code is attached. The program actually finds the right answer for some already solved codes like 0x59E975CE (PARM_EASY), 0x5D9F6F24 (PARM_6PLAYER), and 0x6F4BE7CA (PARM_CHEAT), as well as collisions of different lengths. "unsat" means there are no solutions of the given length. 0x59E975CE 1 unsat 2 unsat 3 unsat 4 'EASY' 5 unsat 6 'Y%KZJ`' 7 'F9H]JC-' 8 '&76FX=$"' 9 '||MQL6K:]' 10 '-K#?S132:L' 11 '9E#}3T{((__' 12 'N.HZ}FC^V~_{' 13 '`!=A["$M99|\'%' 14 '`OX_:^N~{S@<M|' 15 '9B}81"^B""V0A)8' 16 '^}^L%2JERWJ^C{|{' 0x5D9F6F24 1 '6' 2 unsat 3 unsat 4 '2E0~' 5 '>@!,\\' 6 'V9J>\\+' 7 '`7~^1#3' 8 'X2D*@+WD' 9 ">.0|1PC&'" 10 "20MO:'^'BA" 11 '8:#3;~%(?`=' 12 '-:,81?^DR=~(' 13 '(JW01VX[*EB2G' 14 '=~3OX0*~"E~)U1' 15 '^`~NY.)#$;~RTLH' 16 '3[~]<4H=3\\Q)|B7}' 0x6F4BE7CA 1 unsat 2 unsat 3 unsat 4 unsat 5 'CHEAT' 6 'Z##T4I' 7 'XTVJAV/' 8 '1!,"%CV\'' 9 'S|Q.4"@AM' 10 '}<N_6{K42.' 11 'U>;LE|FT=]*' 12 'W(V/{O}N#{MG' 13 '{LA6T%FG~"1FJ' 14 'GW0/C{~{QAFZ`8' 15 ';%Z79T0R2\\^({}~' 16 '+"$_AS-|$""}]$"<' But for the unsolved codes I tried, it just finds random collisions. "OBWAN" for 0xABDD0362 might be plausible, the rest are definitely not the intended strings. 0xABDD0362 1 unsat 2 unsat 3 unsat 4 unsat 5 'OBWAN' 6 '/*5<F^' 7 "1U_T=U'" 8 '2D[Y@".&' 9 '`N01IX?TQ' 10 ',`51%/FLOP' 11 'V:7D.Y.J88"' 12 'P=![(,&MLFQ}' 13 ',}=1|0$0MR!O}' 14 "C.Y(6~G'({O+#$" 15 'Y}^!|QU?JB_F1{{' 16 "E,^6@A18EP(0'%=!" 0xBE79088C 1 unsat 2 unsat 3 unsat 4 unsat 5 'MS"Q_' 6 ']SFY~(' 7 'M7PH7,@' 8 '%/@.:*^:' 9 "'UY9P,GE." 10 '`}7)&FE3?}' 11 '9B}~1`^&NN?' 12 '+OI?FC+;U6VN' 13 '^UF12WF;L?.S,' 14 'OW\\"+0$1#0;?D{' 15 'IB`]Q"|@R1P]`@[' 16 '8K0=&B!T-+`W$\\Q`' Restricting the input to alphabetic characters (rather than the full isgraph character set allowed by the hash algorithm), the discovered solutions are still not meaningful. 0x63CE7584 1 unsat 2 unsat 3 unsat 4 unsat 5 unsat 6 unsat 7 unsat 8 'BEJSVLBD' 9 'UPPBHLNVD' 10 'HTFXNEVSCB' 11 'YSJKBROQCWG' 12 'XCHGECOCIZJF' 13 'EEYODOQOHSLDH' 14 'WMHGPUIBMCJYFC' 15 'LHYFDQPQIWXOCYW' 16 'KBOEMFKNMFYXCLXF' 0xBE79088C 1 unsat 2 unsat 3 unsat 4 unsat 5 unsat 6 unsat 7 'CWNUNHO' 8 'UQHZIWBM' 9 'HKBIDGEDV' 10 'POHEEEXMUA' 11 'EUEJHDXZJXB' 12 'GIJCGTNWWGNM' 13 'IKHJTZQMRDKDC' 14 'CWWKBBDFKJTFDO' 15 'LMMMJUXDYWHTXXF' 16 'GXTTNVXSTLWTYYWX' 0xE0F651B9 1 unsat 2 unsat 3 unsat 4 unsat 5 'QEPOT' 6 'IYTMPD' 7 'UTAMDKK' 8 'WPZEYWLH' 9 'QFFEYFYOM' 10 'JLZHNNLSDC' 11 'LWZKHYJVDEX' 12 'BRZFFZVDEZBA' 13 'AEFLMWRBAEJJM' 14 'AEHZPMQEGENLHJ' 15 'QVEZCRMTARMXEBG' 16 'CMXGQWXUKSNTKEXX' 0xF7867BF0 1 unsat 2 unsat 3 unsat 4 unsat 5 unsat 6 unsat 7 unsat 8 'BFNEYHMI' 9 'YUOWEELIL' 10 'PVDILKVDND' 11 'EFVQLUYXBDT' 12 'MLDDWBNDWBDD' 13 'IVAJGNJCNTDGF' 14 'HKZGTFIQSVSYFB' 15 'CTZUXWQLPVVDEUV' 16 'JJFENREYWBNUADFY' cnc_obfuscate_z3.py.zip
  4. CONQUER.INI supports a number of hidden options activated by key phrases. The key phrases themselves are not stored in the game, but only the 32-bit codes that result from hashing them with a custom hash algorithm. Plausible preimages have been found for most of the hidden options. (Because the output range of the hash algorithm is limited, many inputs hash to the same code, but there is usually one that is obviously the intended one.) Using a list of about 50 million page titles from English Wikipedia and Wiktionary, I found a key phrase I have not yet seen documented. To activate the "Combat" option (PARM_COMBAT), use the key phrase "CHARGE!" (including the exclamation point). Besides the CONQUER.INI options, the same hash-based obfuscation is used for certain command-line options ("FUNPARK" is an example). There are also some commented-out codes for PARM_SPECIAL (i.e., unused synonyms for "FUNPARK"). Using the same wiki title list, I was able to find plausible preimages for many of these as well. Here are preimages for the PARM_* codes in DEFINES.H that are not already documented at TCRF (in a list maintained by Nyerguds) or in the source code: 0xDC57C4B2 PARM_COMBAT "CHARGE!" 0xDFABC23A PARM_CHEATADAM "ADEN" / "ADUN" / "ATEN" / "ATUN" 0xB5B63531 PARM_CHEATBILL "BLUB" 0x9F38A19D PARM_CHEATERIK "SUPER" 0x00532693 PARM_CHEATMIKE "NADE" / "NATE" 0x39D01821 PARM_CHEATPHIL "WIZARD" 0x2E7FE493 PARM_CHEAT_STEVET "BOLLOCKS" 0xD18129F6 PARM_SPECIAL "JURASSIC" 0x2E84E394 PARM_SPECIAL #1 "FUNFUN" 0x85F110A5 PARM_SPECIAL #3 "WHAT_THE" 0x7F65F13C PARM_SPECIAL #4 "ITS_ABOUT_TIME" 0x431F5F61 PARM_SPECIAL #5 "LOST_IN_TIME" 0xE0F651B9 PARM_SPECIAL #7 "GONEWILD" 0xEE1CD37D PARM_SPECIAL #9 "CHOMPCHOMP" That leaves just these codes whose preimages remain unknown: 0xF7867BF0 PARM_BIB 0xBE79088C PARM_CHEATDAVID 0xABDD0362 PARM_CHEATJOE 0xC2AA509B PARM_EDITORERIK 0x63CE7584 PARM_SPECIAL #2 0x10B9683D PARM_SPECIAL #8 To reproduce these results: Download and extract the attached cnc_obfuscate.zip source code. Download the wiki titles: https://dumps.wikimedia.org/enwiki/latest/enwiki-latest-all-titles.gz https://dumps.wikimedia.org/enwiktionary/latest/enwiktionary-latest-all-titles.gz Filter and deduplicate the wiki titles: (gzip -dc enwiki-latest-all-titles.gz | ./filter-wiki-titles.py; gzip -dc enwiktionary-latest-all-titles.gz | ./filter-wiki-titles.py) | sort -u > wordlist.txt Then run the program: cargo run --release < wordlist.txt cnc_obfuscate.zip