lualuau

How does obfuscated code execute a built in function in Lua


I was looking at an online obfuscator (https://luaobfuscator.com/) the other day, obfuscating the code print("Hello, World"). After obfuscating:

local v0=tonumber;local v1=string.byte;local v2=string.char;local v3=string.sub;local v4=string.gsub;local v5=string.rep;local v6=table.concat;local v7=table.insert;local v8=math.ldexp;local v9=getfenv or function() return _ENV;end ;local v10=setmetatable;local v11=pcall;local v12=select;local v13=unpack or table.unpack ;local v14=tonumber;local function v15(v16,v17,...) local v18=1;local v19;v16=v4(v3(v16,5),"..",function(v30) if (v1(v30,2)==81) then v19=v0(v3(v30,1,1));return "";else local v85=0;local v86;while true do if (v85==0) then v86=v2(v0(v30,16));if v19 then local v120=v5(v86,v19);v19=nil;return v120;else return v86;end break;end end end end);local function v20(v31,v32,v33) if v33 then local v87=0 -0 ;local v88;while true do if (v87==(0 -0)) then v88=(v31/(2^(v32-((1 + 0) -0))))%((4 -2)^(((v33-(620 -(555 + 64))) -(v32-(932 -(857 + 74)))) + (878 -(282 + 595)))) ;return v88-(v88%((2206 -(1523 + 114)) -(367 + 201))) ;end end else local v89=(929 -(214 + 641 + 72))^(v32-((1 -0) + 0)) ;return (((v31%(v89 + v89))>=v89) and 1) or 0 ;end end local function v21() local v34=v1(v16,v18,v18);v18=v18 + 1 ;return v34;end local function v22() local v35,v36=v1(v16,v18,v18 + (1067 -(68 + 997)) );v18=v18 + (1272 -(226 + 1044)) ;return (v36 * (1114 -858)) + v35 ;end local function v23() local v37,v38,v39,v40=v1(v16,v18,v18 + (120 -(32 + 85)) );v18=v18 + 4 + 0 ;return (v40 * (3720433 + 13056783)) + (v39 * (66493 -(892 + (120 -55)))) + (v38 * (610 -354)) + v37 ;end local function v24() local v41=v23();local v42=v23();local v43=1 -0 ;local v44=(v20(v42,351 -(87 + 263) ,200 -(67 + 113) ) * ((2 + 0)^32)) + v41 ;local v45=v20(v42,51 -30 ,23 + 8 );local v46=((v20(v42,127 -95 )==(1 + 0)) and  -(953 -(802 + (941 -(368 + 423))))) or 1 ;if (v45==(0 -0)) then if (v44==((0 -0) -0)) then return v46 * (0 + 0) ;else local v110=997 -(915 + 82) ;while true do if (v110==((18 -(10 + 8)) -0)) then v45=3 -2 ;v43=0 + 0 ;break;end end end elseif (v45==(2691 -644)) then return ((v44==(1187 -(1069 + 118))) and (v46 * ((2 -1)/(0 -0)))) or (v46 * NaN) ;end return v8(v46,v45-((620 -(416 + 26)) + 845) ) * (v43 + (v44/((3 -1)^52))) ;end local function v25(v47) local v48;if  not v47 then local v90=0 -0 ;while true do if (v90==(0 + 0)) then v47=v23();if (v47==(0 -0)) then return "";end break;end end end v48=v3(v16,v18,(v18 + v47) -1 );v18=v18 + v47 ;local v49={};for v65=439 -((575 -(44 + 386)) + 293) , #v48 do v49[v65]=v2(v1(v3(v48,v65,v65)));end return v6(v49);end local v26=v23;local function v27(...) return {...},v12("#",...);end local function v28() local v50=(function() return function(v91,v92,v93,v94,v95,v96,v97,v98) local v91=(function() return 1206 -(696 + 510) ;end)();local v92=(function() return;end)();local v93=(function() return;end)();while true do if (v91== #"!") then if (v92== #"]") then v93=(function() return v94()~=(0 -0) ;end)();elseif (v92==(1264 -(1091 + 171))) then v93=(function() return v95();end)();elseif (v92== #"91(") then v93=(function() return v96();end)();end v97[v98]=(function() return v93;end)();break;end if (v91~=(0 + 0)) then else local v116=(function() return 0;end)();local v117=(function() return;end)();while true do if (v116~=(0 -0)) then else v117=(function() return 0 + 0 ;end)();while true do if ((0 + 0)==v117) then v92=(function() return v94();end)();v93=(function() return nil;end)();v117=(function() return 1;end)();end if ((255 -(163 + 91))~=v117) then else v91=(function() return  #">";end)();break;end end break;end end end end return v91,v92,v93,v94,v95,v96,v97,v98;end;end)();local v51=(function() return function(v99,v100,v101) local v102=(function() return 1930 -(1869 + 61) ;end)();while true do if (v102~=0) then else local v118=(function() return 0 + 0 ;end)();while true do if (v118~=(0 + 0)) then else v99[v100-#"[" ]=(function() return v101();end)();return v99,v100,v101;end end end end end;end)();local v52=(function() return {};end)();local v53=(function() return {};end)();local v54=(function() return {};end)();local v55=(function() return {v52,v53,nil,v54};end)();local v56=(function() return v23();end)();local v57=(function() return {};end)();for v67= #" ",v56 do FlatIdent_5ED46,Type,Cons,v21,v24,v25,v57,v67=(function() return v50(FlatIdent_5ED46,Type,Cons,v21,v24,v25,v57,v67);end)();end v55[ #"asd"]=(function() return v21();end)();for v68= #"!",v23() do local v69=(function() return v21();end)();if (v20(v69, #">", #":")~=(0 -0)) then else local v106=(function() return 0 + 0 ;end)();local v107=(function() return;end)();local v108=(function() return;end)();local v109=(function() return;end)();while true do if (v106~=(0 -0)) then else v107=(function() return v20(v69,204 -(14 + 188) , #"91(");end)();v108=(function() return v20(v69, #"0313",6);end)();v106=(function() return 1 + 0 ;end)();end if (v106~=1) then else v109=(function() return {v22(),v22(),nil,nil};end)();if (v107==(0 + 0)) then local v122=(function() return 0 + 0 ;end)();local v123=(function() return;end)();while true do if (v122==(0 + 0)) then v123=(function() return 0 -0 ;end)();while true do if ((0 -0)==v123) then v109[ #"xxx"]=(function() return v22();end)();v109[ #"asd1"]=(function() return v22();end)();break;end end break;end end elseif (v107== #"<") then v109[ #"asd"]=(function() return v23();end)();elseif (v107==2) then v109[ #"-19"]=(function() return v23() -((1476 -(1329 + 145))^16) ;end)();elseif (v107== #"19(") then local v141=(function() return 971 -(140 + 831) ;end)();local v142=(function() return;end)();while true do if (v141~=(1850 -(1409 + 441))) then else v142=(function() return 718 -(15 + 703) ;end)();while true do if (v142==0) then v109[ #"91("]=(function() return v23() -(2^16) ;end)();v109[ #".dev"]=(function() return v22();end)();break;end end break;end end end v106=(function() return 1 + 1 ;end)();end if (v106~=(441 -(262 + 176))) then else if (v20(v108, #"91(", #"91(")~= #",") then else v109[ #"0313"]=(function() return v57[v109[ #"xnxx"]];end)();end v52[v68]=(function() return v109;end)();break;end if (v106~=(1723 -(345 + 1376))) then else if (v20(v108, #"{", #"~")== #",") then v109[690 -(198 + 490) ]=(function() return v57[v109[8 -6 ]];end)();end if (v20(v108,4 -2 ,2)== #"|") then v109[ #"91("]=(function() return v57[v109[ #"gha"]];end)();end v106=(function() return 3;end)();end end end end for v70= #"\\",v23() do v53,v70,v28=(function() return v51(v53,v70,v28);end)();end return v55;end local function v29(v59,v60,v61) local v62=v59[397 -(115 + 281) ];local v63=v59[4 -2 ];local v64=v59[3 + 0 ];return function(...) local v71=v62;local v72=v63;local v73=v64;local v74=v27;local v75=2 -1 ;local v76= -(3 -2);local v77={};local v78={...};local v79=v12("#",...) -1 ;local v80={};local v81={};for v103=0,v79 do if (v103>=v73) then v77[v103-v73 ]=v78[v103 + (868 -((2186 -(1373 + 263)) + 317)) ];else v81[v103]=v78[v103 + (1 -0) ];end end local v82=(v79-v73) + 1 ;local v83;local v84;while true do v83=v71[v75];v84=v83[1 -(1000 -(451 + 549)) ];if ((v84<=(8 -5)) or (165>=3492)) then if (v84<=((91 + 195) -(134 + 151))) then if (v84==(1665 -(970 + 695))) then local v127=v83[3 -(1 -0) ];v81[v127](v81[v127 + (1991 -((977 -395) + 1408)) ]);else do return;end end elseif (v84==(6 -4)) then v81[v83[2]]=v61[v83[3 -0 ]];else do return;end end elseif ((3949<4856) and (v84<=5)) then if (v84==4) then local v130=v83[7 -(1389 -(746 + 638)) ];v81[v130](v81[v130 + (1825 -(1195 + 629)) ]);else v81[v83[2 -0 ]]=v83[244 -(71 + 116 + 54) ];end elseif (v84>(786 -(162 + 618))) then v81[v83[2 + 0 ]]=v83[2 + 1 ];else v81[v83[3 -1 ]]=v61[v83[4 -1 ]];end v75=v75 + 1 + 0 ;end end;end return v29(v28(),{},v17)(...);end return v15("LOL!023Q0003053Q007072696E74030C3Q0048652Q6C6F2C20576F726C6400043Q0012023Q00013Q001205000100028Q000200012Q00033Q00017Q00",v9(),...);

I searched for any references to the print function but found none. Upon running the newly obfuscated code, though, the console still prints Hello, World.

I tried looking up some infos on the Internet and what I found is just mainly about proxy functions, like for example

local p = print
p("Hello, World")

But the obfuscated code does not have any references to print to it, so I think the obfuscator have done something differently.

Does anyone know how the code execute a built in function, in this case, print, without any (visible) references to it at all?


Solution

  • I am not planning to study how this obfuscator actually works. Here is a way to explain how to call print without explicitly referencing it.

    In Lua, you can use the load function (or loadstring in Lua 5.1) to execute a string as Lua code.

    load('print("Hello world")')()
    

    Since the execution is a string, which is a byte sequence, you can completely obfuscate each character by concatenating them one by one. For example, using the string.char function:

    local v0 = string.char(0x70,0x72,0x69,0x6E,0x74,0x28,0x22,0x48,0x65,0x6C,0x6C,0x6F,
                           0x2C,0x20,0x57,0x6F,0x72,0x6C,0x64,0x22,0x29)
    -- v0 equals 'print("Hello world")'
    load(v0)()
    

    And you can futher obfuscate the load function:

    local v1 = _ENV[string.char(0x6C,0x6F,0x61,0x64)]
    v1(v0)()
    

    ....