First get an ascii signature like this with only 4 differents char (ignore newlines)
local tbl =[[
___ ________ ________ ________ ___ ___
|\ \ |\ __ \|\ ___ \|\ ____\|\ \ |\ \
\ \ \ \ \ \|\ \ \ \\ \ \ \ \___|\ \ \\_\ \
\ \ \ \ \ ____\ \ \\ \ \ \ \____\ \______ \
\ \ \____\ \ \___|\ \ \\ \ \ \ ___ \|_____|\ \
\ \_______\ \__\ \ \__\\ \__\ \_______\ \ \__\
\|_______|\|__| \|__| \|__|\|_______| \|__|]]
Then remove the newlines
local code = [[ ___ ________ ________ ________ ___ ___ |\ \ |\ __ \|\ ___ \|\ ____\|\ \ |\ \ \ \ \ \ \ \|\ \ \ \\ \ \ \ \___|\ \ \\_\ \ \ \ \ \ \ ____\ \ \\ \ \ \ \____\ \______ \ \ \ \____\ \ \___|\ \ \\ \ \ \ ___ \|_____|\ \ \ \_______\ \__\ \ \__\\ \__\ \_______\ \ \__\ \|_______|\|__| \|__| \|__|\|_______| \|__|]]
For the sake of simplicity, make two table (by index and by value) with the ascii code of each char.
local letters = {}
letters[124] = 0 -- |
letters[95] = 1 -- _
letters[32] = 2 --
letters[92] = 3 -- \
local alt_letters = {}
alt_letters[0] = 124-- |
alt_letters[1] = 95-- _
alt_letters[2] = 32 --
alt_letters[3] = 92-- \
Each letter will have it's own identifier from 0 to 3, it only takes 2 bits so we can store 16 letters per 32 bits integers
local i = 1
local intStacked = 0
while (i <= code:len()) do
if i % 16 == 1 then
intStacked = 0
end
local char = code:sub(i, i):byte()
if i % 16 == 0 then
intStacked = intStacked + letters[char]
print(intStacked)
else
intStacked = intStacked + bit.lshift(letters[char], (16 - (i % 16)) * 2)
if (i == code:len()) then -- we may have integer not entirely filled with 16 letters when the meet the string end so we fill it
while ((i % 16) ~= 0) do
i = i + 1
intStacked = intStacked + bit.lshift(2, (16 - (i % 16)) * 2)
end
print(intStacked)
break;
end
end
i = i + 1
end
If we print intStacked when i%16 == 0 we can get this table :
local tblint = {
-1783977643,
1452627306,
1431676266,
1453996778,
-1549178054,
-1783829931,
-826741846,
-289755413,
988724971,
-1160430673,
2062203822,
-1413835397,
-1346703691,
1591039674,
-1360308754,
-1253118994,
-1146787051,
1403956149,
1434148523,
-1241817387,
1441443543,
-1429908148,
1386923308,
1393906986,
-1424708950
}
Then we can print back the signature using :
for k, v in pairs(tblint) do
local mask = -1073741824
local shift = 30
local count = 1
while shift ~= -2 do
io.write(string.char(alt_letters[bit.rshift(bit.band(v, mask), shift)]))
if ((k - 1) * 16 + count) % 56 == 0 then
io.write("\n")
end
mask = bit.rshift(mask, 2)
shift = shift - 2
count = count + 1
end
end
io.write("\n")
(56 was the line len, so we hardcode the newline to save data in the 32bits integers)
If we want to go even deeper and store the data into a string we can split the ints in 8 bits chars to hold in a string of 100 chars
local function stringFromInt(int)
local d = bit.band(int, 0x000000ff)
local c = bit.rshift(bit.band(int, 0x0000ff00), 8)
local b = bit.rshift(bit.band(int, 0x00ff0000), 16)
local a = bit.rshift(bit.band(int, 0xff000000), 24)
print(a, b, c, d)
end
local inttable = {149, 170, 169, 85,
86, 149, 85, 106,
85, 85, 165, 106,
86, 170, 58, 234,
163, 169, 107, 58,
149, 172, 234, 85,
206, 184, 235, 170,
238, 186, 174, 235,
58, 238, 190, 235,
186, 213, 59, 175,
122, 234, 187, 174,
171, 186, 149, 123,
175, 186, 238, 181,
94, 213, 86, 186,
174, 235, 85, 238,
181, 78, 235, 238,
187, 165, 107, 21,
83, 174, 171, 181,
85, 123, 94, 171,
181, 251, 94, 213,
85, 234, 174, 215,
170, 197, 85, 76,
82, 170, 197, 44,
83, 21, 85, 42,
171, 20, 170, 170,}
And we get this string (Some of them are escaped because of encoding mess)
local pro_str = "\1\2\3UV\6UjUU\11jV\14:\16\17\18k:\21\22\23U\25\26\27\28\29\30\31\32:\34\35\36\37\38;\40z\42\43\44\45\46\47{\49\50\51\52^\54V\56\57\58U\60\61N\63\64\65\66k\68S\70\71\72U{^\76\77\78^\80U\82\83\84\85\86ULR\90\91,S\94U*\97\98\99\100"
And we can print it with
local len = pro_str:len()
local i = 1
while i < len do
local mask = 192 -- 1100 0000 binary mask
local shift = 6
local count = 1
while shift ~= -2 do
io.write(string.char(alt_letters[bit.rshift(bit.band(pro_str:byte(i,i), mask), shift)]))
if ((i - 1) * 4 + count) % 56 == 0 then
io.write("\n")
end
mask = bit.rshift(mask, 2)
shift = shift - 2
count = count + 1
end
i =i + 1
end
io.write("\n")
Let's now obfuscate the data, but before here is our now base code :
local alt_letters = {}
alt_letters[0] = 124 -- |
alt_letters[1] = 95 -- _
alt_letters[2] = 32 --
alt_letters[3] = 92 -- \
local pro_str = "\149\170\169UV\149UjUU\165jV\170:\234\163\169k:\149\172\234U\206\184\235\170\238\186\174\235:\238\190\235\186\213;\175z\234\187\174\171\186\149{\175\186\238\181^\213V\186\174\235U\238\181N\235\238\187\165k\21S\174\171\181U{^\171\181\251^\213U\234\174\215\170\197ULR\170\197,S\21U*\171\20\170\170"
local len = pro_str:len()
local i = 1
while i < len do
local mask = 192
local shift = 6
local count = 1
while shift ~= -2 do
io.write(string.char(alt_letters[bit.rshift(bit.band(pro_str:byte(i, i), mask), shift)]))
if ((i - 1) * 4 + count) % 56 == 0 then
io.write("\n")
end
mask = bit.rshift(mask, 2)
shift = shift - 2
count = count + 1
end
i = i + 1
end
io.write("\n")
The first thing I'll do is to fit all the following less-than-8-bits integers into one 32 bit integer
local alt_letters = {}
alt_letters[0] = 124 -- |
alt_letters[1] = 95 -- _
alt_letters[2] = 32 --
alt_letters[3] = 92 -- \
124 is 7C in hexadecimal
95 is 5F
32 is 20
92 is 5C
local alt_letter = 0x5C205F7C
Before converting the whole code to the new 32bit integer system let's make it a bit more readable :
local addr = bit.rshift(bit.band(pro_str:byte(i, i), mask), shift)
io.write(
string.char(alt_letters[addr])
)
And now converted to use the one 32bit integer :
local letter = 0x5C205F7C
local data = "\149\170\169UV\149UjUU\165jV\170:꣩k:\149\172\234Uθ\235\170\238\186\174\235:\238\190\235\186\213;\175z껮\171\186\149{\175\186\238\181^\213V\186\174\235U\238\181N\235k\21S\174\171\181U{^\171\181\251^\213U\234\174\215\170\197ULR\170\197,S\21U*\171\20\170\170"
local i = 1;
while i < data:len() do
local mask = 192
local shift = 6
local count = 1
while shift ~= -2 do
local addr = bit.rshift(bit.band(data:byte(i, i), mask), shift)
local dbMask = bit.lshift(0x000000ff, addr * 8)
local value = bit.band(letter, dbMask)
value = bit.rshift(value, addr * 8)
io.write(string.char(value))
if ((i - 1) * 4 + count) % 56 == 0 then
io.write("\n")
end
mask = bit.rshift(mask, 2)
shift = shift - 2
count = count + 1
end
i = i + 1
end
io.write("\n")
Then I can get rid of the count var and replace it by (4-shift/2)
Final code :
Data string is has 100 ASCII characters or 84 UTF(8?16?32) characters.
And prints 400 characters, 25% ratio compression.