Skip to content

Instantly share code, notes, and snippets.

@ExtReMLapin
Last active September 10, 2019 15:08
Show Gist options
  • Save ExtReMLapin/01bdff7b011399d7f340d77151b78617 to your computer and use it in GitHub Desktop.
Save ExtReMLapin/01bdff7b011399d7f340d77151b78617 to your computer and use it in GitHub Desktop.
Generate a lua (jit) compressed signature

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\234\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\235k\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)


@ExtReMLapin
Copy link
Author

ExtReMLapin commented Aug 20, 2019

Final code :

local letter = 0x5C205F7C
local data = "\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 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")

Data string is has 100 ASCII characters or 84 UTF(8?16?32) characters.

And prints 400 characters, 25% ratio compression.

@ExtReMLapin
Copy link
Author

ExtReMLapin commented Aug 20, 2019

On his website, @a1k0n goes from a key of 110 chars

(Yes I wanted to do mine after seeing his signature)

##K#8(38D-##C]L5870.X7\\M_b;90\\MC-M/NZGB6Q,I0VGB6a0FbN<VG.6Q\\bNb7^@X=N@XQaOVX:^]NX=:Z8PY]B:>PNY8^$#SM):XA`

To a signature of 288 characters

               __     _____      _____   ___                  __
 ___ ____  ___/ /_ __/ ___ \___ <  / /__/ _ \___    ___  ___ / /_
/ _ `/ _ \/ _  / // / / _ `/ _ `/ /  '_/ // / _ \_ / _ \/ -_) __/
\_,_/_//_/\_,_/\_, /\ \_,_/\_,_/_/_/\_\\___/_//_(_)_//_/\__/\__/
              /___/  \___/

Which is only a 38% compression

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment