和平精英模拟器检测分析

发表于:2021-02-26

核心原理:读取elf头,判断机器类型是否为x86

过检测方法(以雷电模拟器4为例):

第一种方法(推荐使用这种方法):

修改系统/system/lib中所有so的机器类型,改为arm,但是这样改了之后,加载so会报 unexpected e_machine,解决办法就是去掉系统加载so过程中判断机器类型的代码段。

打开android7.1源码中linker_phdr.cpp,在VerifyElfHearder函数开头加上return true; 这样linker链接的时候就不会去判断elf头,也就不会报错,然后编译linker,把编译出来的linker替换掉模拟器中的/system/bin/linker,再重启模拟器即可。以后直接打开游戏就能过检测了。

另外一种方法(内存改错了,有几率会10):游戏打开后,再去修改系统的so,也能过检测,但是会被封1年。这是因为游戏打开的时候,/system/lib中的so会映射到内存中,相当于复制一份,还需要修改游戏内存中so的机器类型,可以通过/proc/$pid/maps文件查看游戏加载的so

 

 

通过对游戏Lua对抗方案的代码段进行inline hook,可以导出对应lua文件。

例如:其中的detect_chicken就是用来检测鸡腿挂的。Scan_emulator_release用来检测模拟器,可以动态更新检测策略。Emu_info_collect用来收集模拟器信息,估计是用来追封用的吧。


以下是 scan_emulator_release lua代码:

local function is_null(p)

    if p == 0 or p == nil then

       return true

    end

    return false

end

 

local function find_str(s1, s2)

    local n1,n2 = string.find(s1, s2, 1, true)

    if (n1 ~= nil and n2 ~= nil)

    then

       return true

    end

    return false

end

 

local function str_startswith(s1, s2)

    local n1, n2 = string.find(s1, s2, 1, true)

    if (n1 ~= nil and n2 ~= nil and n1 == 1)

    then

       return true

    end

    return false

end

 

local function str_endswith(s1, s2)

    local n1, n2 = string.find(s1, s2, 1, true)

    if (n1 ~= nil and n2 ~= nil and n2 == #s1)

    then

       return true

    end

    return false

end

 

local function open_file_exists(s_path)

    local is_valid_path = false

    if (str_startswith(s_path,"/system/") or

       str_startswith(s_path,"/data/") or

       str_startswith(s_path,"/lib/") or

       str_startswith(s_path,"/sbin/") or

       str_startswith(s_path,"/sys/")) and

       (str_endswith(s_path,".so")) then

       is_valid_path = true

    end

    if not is_valid_path then

       return false

    end

    local f = io.open(s_path, "r")

    if f then io.close(f) end

    return f ~= nil

end

 

local function bin2hex(s)

    s=string.gsub(s,"(.)",function (x) return string.format("%02X",string.byte(x)) end)

    return s

end

 

local function basename(str)

    local name = string.gsub(str, "(.*[\\/])(.*)", "%2")

    return name

end

 

local function check_elf_file_x86_emulator(file_path)

    local ret = 0

    if not open_file_exists(file_path) then

       return ret

    end

    local f = io.open(file_path,'rb')

    if f then

       local sizes = f:seek("end")

       if sizes > 1024 then

           f:seek("set")

           local content = f:read(4)

           local hexstr = bin2hex(content)

           if hexstr == "7F454C46" then

              local is_32_64 = f:read(1)

              hexstr = bin2hex(is_32_64)

              if hexstr == "01" then

                  f:seek("set",18)

                  local cpu_type = f:read(2)

                  hexstr = bin2hex(cpu_type)

                  if hexstr == "0300" then

                     ret = 1

                  end

              end

           end

       end

       f:close()

    end

    return ret

end

 

local function get_int_value(key, deft)

    local value = TssSDK.get_value_by_key(key)

    if not is_null(value) then

       local int_val = tonumber(value)

       if int_val then

           return int_val

       end

    end

    return deft

end

 

local g_mark_scan_flag = "llua_ser_20200421_scanned"

local g_module_cnt = 0

local g_libc_files_cnt = 0

local g_has_x86_file = 0

local g_x86_file_name = ""

local g_tp_ver = ""

local g_libhoudini_file_name = ""

local g_libhoudini_file_size = 0

 

local function save_scan_result()

    TssSDK.set_key_and_value("LEMC", ""..g_module_cnt)

    TssSDK.set_key_and_value("LEFC", ""..g_libc_files_cnt)

    TssSDK.set_key_and_value("LEHX", ""..g_has_x86_file)

    TssSDK.set_key_and_value("LEXN", g_x86_file_name)

    TssSDK.set_key_and_value("LETV", g_tp_ver)

    TssSDK.set_key_and_value("LENM", g_libhoudini_file_name)

    TssSDK.set_key_and_value("LESZ", ""..g_libhoudini_file_size)

end

 

local function load_scan_result()

    g_module_cnt = get_int_value("LEMC", 0)

    g_libc_files_cnt = get_int_value("LEFC", 0)

    g_has_x86_file = get_int_value("LEHX", 0)

    g_x86_file_name = TssSDK.get_value_by_key("LEXN")

    g_tp_ver = TssSDK.get_value_by_key("LETV")

    g_libhoudini_file_name = TssSDK.get_value_by_key("LENM")

    g_libhoudini_file_size = get_int_value("LESZ", 0)

end

 

local function get_tp_ver()

    local try_cnt = 0

    local err_msg = ""

    local err_code = 0

    local dev_path = "/dev/virtpipe-sec"

    while (try_cnt < 3) do

       try_cnt = try_cnt + 1

       local ret, acode = TssSDK.access(dev_path)

       if (ret ~= 0) then

           err_msg = "unfound"

           err_code = acode

       else

           local session = TssSDK.OpenWBSession()

           if (is_null(session)) then

              err_msg = "open_failed"

              local file, err, ocode = io.open(dev_path, "r+")

              if (file) then

                  file:close()

              end

              if (ocode ~= nil) then

                  err_code = ocode

              else

                  err_code = 0

              end

           else

              local tp_ver = TssSDK.SendWBCmd(session, "func=WB_GetTPShellVersion")

              TssSDK.CloseWBSession(session)

              if (is_null(tp_ver) or type(tp_ver) ~= "string") then

                  err_msg = "get_failed"

                  err_code = 0

              else

                  return tp_ver.."-"..tostring(0)

              end

           end

       end

       TssSDK.sleep(1000)

    end

    return err_msg.."-"..tostring(err_code)

end

 

local function scan_emulator()

    local try_cnt = 5

    local handle = TssSDK.open_tphm()

    while (try_cnt > 0 and is_null(handle)) do

       TssSDK.sleep(1000)

       handle = TssSDK.open_tphm()

       try_cnt = try_cnt - 1

    end

    if is_null(handle) then

       return

    end

 

    local libc_name = "libc.so"

    local libc_files = {}

    local libhoudini_name = "houdini"

    while g_module_cnt < 10000 do

       local module_info = TssSDK.enum_tphm(handle)

       if is_null(module_info) then

           break

       end

       g_module_cnt = g_module_cnt + 1

 

       local module_name = TssSDK.get_tphm_name(module_info)

       local module_size = TssSDK.get_tphm_size(module_info)

       if not is_null(module_name) and not is_null(module_size) then

           local file_name = basename(module_name)

           if find_str(file_name, libc_name) then

              if libc_files[module_name] == nil then

                  g_libc_files_cnt = g_libc_files_cnt + 1

                  libc_files[module_name] = {}

               end

           elseif find_str(file_name, libhoudini_name) then

              if g_libhoudini_file_name == "" then

                  g_libhoudini_file_size = module_size

                  g_libhoudini_file_name = module_name

              end

           end

 

           if g_has_x86_file == 0 then

              g_has_x86_file = check_elf_file_x86_emulator(module_name)

              if g_has_x86_file == 1 then

                  g_x86_file_name = module_name

                  g_tp_ver = get_tp_ver()

              end

           end

       end

    end

    save_scan_result()

    TssSDK.close_tphm(handle)

end

 

local function get_report_cnt()

    local cnt = get_int_value("LERC", 0)

    cnt = cnt % 1000 + 1

    TssSDK.set_key_and_value("LERC", ""..cnt)

    return cnt

end

 

local function report_emulator()

    local emu_name = TssSDK.QueryStr("emulator_name")

    local maybe_emu = 1

    if (emu_name == "NotEmulator")

    then

       maybe_emu = 0

    end

 

    local is_download = TssSDK.IsEnabled("simulate", 0)

    local report_cnt = get_report_cnt()

    local info = tostring(g_has_x86_file).."|"..tostring(g_libc_files_cnt).."|"..tostring(g_libhoudini_file_size).."|"..tostring(maybe_emu).."|"..tostring(report_cnt)

    TssSDK.set_key_and_value("li_emu", info)

    TssSDK.ReportQosByGame(8, 2020061700, g_has_x86_file, g_libc_files_cnt, g_libhoudini_file_size, g_module_cnt, maybe_emu, is_download, report_cnt, 2, emu_name..','..g_libhoudini_file_name, g_tp_ver..','..g_x86_file_name)

end

 

local function has_scanned()

    return (not is_null(TssSDK.get_value_by_key(g_mark_scan_flag)))

end

 

local function set_scanned()

    TssSDK.set_key_and_value(g_mark_scan_flag, "1")

end

 

local function do_exec()

    if TssSDK.get_platform() ~= 0 then

       return

    end

 

    if (has_scanned()) then

       load_scan_result()

    else

       scan_emulator()

       set_scanned()

    end

 

    report_emulator()

end

 

local function exec()

    local ver = TssSDK.get_sdk_version()

    if is_null(ver) then

       return

    end

 

    local target_ver = "3.7.13.548645"

    if ver < target_ver then

       return

    end

 

    do_exec()

end

 

exec()

这里就不公开其它lua文件了,你们自己去研究。


评论(1)
  • 2purngrgxmj

    2021-10-09

    Lua文件是怎么导出来的,求解

    点赞(0) 回复
    • 举报