吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 59946|回复: 112
收起左侧

[原创] lua脚本解密6:YIC加密

  [复制链接]
Ganlv 发表于 2018-5-19 17:35
本帖最后由 Ganlv 于 2018-5-22 15:56 编辑

系列教程

样本

两个求破贴

样本 1 来源:https://www.52pojie.cn/forum.php?mod=redirect&goto=findpost&ptid=737347&pid=20098089

下载地址:https://pan.baidu.com/s/1tKLaRdyQpz6ORmYCLZ48Aw

01.jpg

--YIC新加密算法
print('加密由一炮、&@yic77455提供。\n如未能进入,可能密码错误!\n'..'加群免费获取')
function Yicjm(key,code)
   return (code:gsub('..', function (h) return string.char((tonumber(h,16)+256-13 - key + 999999*256)%256) end))
end
local v_value=gg.prompt({'加群免费获取'},{[1]=0},{[1]='number'})
gg.toast('正在加载脚本……')
pcall(Yicjm(v_value[1], '1B1D14191FD3D2FCFCDEDEE0DBE1DCE4......'))

样本 2 来源:https://www.52pojie.cn/forum.php?mod=redirect&goto=findpost&ptid=737347&pid=20161673

下载地址:https://www.lanzouj.com/i11pd4j

02.jpg

--YIC新加密算法
goto aa
::aa::
gg.clearResults()
gg.searchNumber('1;1', gg.TYPE_AUTO, false, gg.SIGN_EQUAL, 0, -1)
gg.getResults(2000)
gg.toast('过保护已开启')
print('加密由饭、&@yic77455提供。\n如未能进入,可能密码错误!\n'..'友情更新刺激战场5.12最新版')
function Yicjm(key,code)
   return (code:gsub('..', function (h) return string.char((tonumber(h,16)+256-13 - key + 999999*256)%256) end))
end
local v_value=gg.prompt({'友情更新刺激战场5.12最新版'},{[1]=0},{[1]='number'})
gg.toast('正在加载脚本……')
pcall(Yicjm(v_value[1], 'B3C2BBB0C1B6BCBB6DB0B5B2B0B8A1B6......'))

这两个样本都可以“YIC加密”的字样。

分析

看上去这个关键代码 string.char((tonumber(h,16)+256-13 - key + 999999*256)%256) 的解码算法不是特别麻烦,但是还是和 lua脚本解密5:RC4加密穷举秘钥 一样,没有秘钥是解不了的,必须得穷举。

因为这个解码算法比较简单,所以我们仔细分析一下。

如果你真的想学,你最好先去简单的学一下 Lua 语言。

这里假设你应该是可以看出哪段代码是我们解密的关键之处,哪段代码与解密无关。

我尽量用最通俗的语言解释。

碰到 Lua 相关语法,我会默认你已知(请自行去学习 Lua 的相关知识,自行去查阅手册)。

这个算法并不需要很高的数学水平,只要会算术就可以理解,小学生只要会加减乘除求余数就可以理解。

代码概况

关键代码如下:

function Yicjm(key,code)
   return (code:gsub('..', function (h) return string.char((tonumber(h,16)+256-13 - key + 999999*256)%256) end))
end
local v_value=gg.prompt({'友情更新刺激战场5.12最新版'},{[1]=0},{[1]='number'})
gg.toast('正在加载脚本……')
pcall(Yicjm(v_value[1], 'B3C2BBB0C1B6BCBB6DB0B5B2B0B8A1B6......'))

这段代码的意思大概就是

  1. 定义一个解密函数 Yicjm
  2. gg修改器读取一个数值存到 v_value
  3. 提示正在加载脚本
  4. 然后用你刚才输入的那个数值 v_value[1] 作为 Yicjm 函数的 key 参数来解密 'B3C2BBB0C1B6BCBB6DB0B5B2B0B8A1B6......' 这个字符串,得到真正的代码,然后执行。

解码函数

接下来我们来分析这个 Yicjm 函数。

这里的 key 就是秘钥了,code 是一个字符串,code 的形式就是一个十六进制字符串表示的数据。

然后我们就要分析这个算法了。

code:gsub('..', function (h) return string.char((tonumber(h,16)+256-13 - key + 999999*256)%256) end)

code:gsub 中的冒号(:)是 Lua 面向对象编程的一个语法糖,因为 code 是一个字符串(string)类型的变量,所以这句话相当于

string.gsub(code, '..', return string.char((tonumber(h,16)+256-13 - key + 999999*256)%256) end)

我们去查一下 gsub 的用法

https://www.lua.org/manual/5.2/manual.html#pdf-string.gsub

string.gsub (s, pattern, repl [, n])

Returns a copy of s in which all (or the first n, if given) occurrences of the pattern have been replaced by a replacement string specified by repl, which can be a string, a table, or a function. gsub also returns, as its second value, the total number of matches that occurred. The name gsub comes from Global SUBstitution.

If repl is a string, then its value is used for replacement. The character % works as an escape character: any sequence in repl of the form %d, with d between 1 and 9, stands for the value of the d-th captured substring. The sequence %0 stands for the whole match. The sequence %% stands for a single %.

If repl is a table, then the table is queried for every match, using the first capture as the key.

If repl is a function, then this function is called every time a match occurs, with all captured substrings passed as arguments, in order.

In any case, if the pattern specifies no captures, then it behaves as if the whole pattern was inside a capture.

If the value returned by the table query or by the function call is a string or a number, then it is used as the replacement string; otherwise, if it is false or nil, then there is no replacement (that is, the original match is kept in the string).

Here are some examples:

     x = string.gsub("hello world", "(%w+)", "%1 %1")
     --> x="hello hello world world"

     x = string.gsub("hello world", "%w+", "%0 %0", 1)
     --> x="hello hello world"

     x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")
     --> x="world hello Lua from"

     x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv)
     --> x="home = /home/roberto, user = roberto"

     x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)
           return load(s)()
         end)
     --> x="4+5 = 9"

     local t = {name="lua", version="5.2"}
     x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
     --> x="lua-5.2.tar.gz"

这个解码函数使用的是 “repl is a function” 这种用法,每次匹配到样式,都会调用后面的函数,然后替换成返回值。

这里我把原文复制过来不是为了凑字数,而是为了说明如何查阅官方文档。

至于 Lua 的字符串匹配语法请自行参考官方手册 https://www.lua.org/manual/5.2/manual.html#6.4.1

这里的匹配样式就是 '..',两个点就是两个字符,就是两个十六进制数,或者说就是 1 个字节的十六进制表示。

替换 callback 算法分析

function (h)
    return string.char((tonumber(h,16)+256-13 - key + 999999*256)%256)
end

这里的 h 就是一个 2 个字符的字符串,比如 'B3''C2' 等等(以样本 2 的加密部分 'B3C2BBB0C1B6BCBB6DB0B5B2B0B8A1B6......' 为例)。

tonumber(h,16) 这个函数就是把字符串以 16 进制的形式转换为数字,'B3' 会被转换成为 179,我们暂时把这个数值记为 b(代表 byte)。

03.jpg

然后最关键的部分来了,看这段算法:

(b + 256 - 13 - key + 999999 * 256) % 256

这是一个求余数的运算。首先,它似乎等于(这个应该能看懂吧):

(b - 13 - key + 1000000 * 256) % 256

根据同余定理,它等于:

(b - 13 - key) % 256

如果你不懂的话,简单的说明一下:3 % 7 = 3, 10 % 7 = 3, 17 % 7 = 3

发现什么规律了吗?如果 x % 256 = y,那么 (x + 1000000 * 256) % 256 = y 也成立。

因上面的式子是一个求除以 256 的余数,那么 key 的穷举范围只需要取 0 ~ 255 就够了,即使你想将 key 取在更大的范围,比如 key 取为 257,结果一定和 key 取 1 的时候是一样的。

同时我们也知道了,式子的结果也一定是 0 ~ 255 的范围,这个结果恰好被 string.char 转换成为字符。

解码

所以,请用一句话总结一下上面我们分析得到的结论是什么?


key 的穷举范围取 0 ~ 255

然后我们就写一个穷举的代码

function Yicjm(key,code)
    return (code:gsub('..', function (h) return string.char((tonumber(h,16)+256-13 - key + 999999*256)%256) end))
end
for key = 0, 255 do
    print(key)
    local code = Yicjm(key, 'B3C2BBB0C1B6BCBB6DB0B5B2B0B8A1B6')
    if (string.find(code, "gg")
        and string.find(code, "if")
        and string.find(code, "then")
        and string.find(code, "end")) then
        print(key .. " may be the key.")
        file = io.open("2.lua", "w")
        file:write(code)
        file:close()
        break
    end
end

04.jpg

这里就是检测了解码中的代码中是否包含 ggifthenend 这类特殊关键字,如果包含则解码成功了,输出到 2.lua

05.jpg
06.jpg

成果

样本 1 成果

07.jpg

样本 2 成果

08.jpg

总结

秘钥量太小,轻易就能穷举出来,总之这个加密是非常不负责任的。

样本 3

样本 3 求破贴

09.jpg

我下载看了一下,的确按照我的方法是无法找到秘钥的,为什么呢?

原因很简单,因为解密之后的代码不包含 ggifthenend 这类特殊关键字。

如果你尝试判断是否包括 load 关键字,那么你可以很轻松的得到秘钥。

for key = 0, 255 do
    print(key)
    local code = Yicjm(key, "4E4E4E71719D8D8729CEE429F3CA2BF2")
    if (string.find(code, 'load')) then
        print(key .. " YIC decode OK.")
        file = io.open("2.lua", "w")
        file:write(code)
        file:close()
        break
    end
end

其实是在不行你就挨个试嘛...

那么有没有什么更好的方法呢?

既然读取脚本用的是 load 函数,这个函数在读取字符串成功时会返回读取结果(也就是一个函数),失败时则返回 nil 和错误信息,那么我们可以用这个函数来判断,解密之后的函数能否被读取运行。

for key = 0, 255 do
    print(key)
    local code = Yicjm(key, "4E4E4E71719D8D8729CEE429F3CA2BF2")
    local func, err = load(code)
    if (func) then
        print(key .. " YIC decode OK.")
        file = io.open("2.lua", "w")
        file:write(code)
        file:close()
        break
    end
end

这回可就是“为所欲为”了,即使出现 precompiled chunk 也不怕了,load 函数会自动处理的。

注意:对于 lua 5.1 请使用 loadstring 函数

样本 3 成果

10.jpg

这只是解密出的第 1 层,样本 3 的这个作者硬是用同样一个算法加密了 7 层,中间每层还把密码密码给出来了。

作者似乎不知道“同余”这种东西,9398793987 % 256(也就是 35) 得到的结果是一样的,根本用不着那么长的密码。

11.jpg

能破的人不在乎多几层,破不了的人一层加密就足够了,真有意思。

相关链接

附件

3 个样本,以及使用到的代码

examples.7z (21.18 KB, 下载次数: 256)

免费评分

参与人数 18威望 +1 吾爱币 +39 热心值 +16 收起 理由
shan999abc + 1 <font style="vertical-align: inherit;"><font style=
my8093 + 1 + 1 用心讨论,共获提升!
thomaszjh + 1 + 1 谢谢@Thanks!
Javaer + 1 + 1 鼓励转贴优秀软件安全工具和文档!
良辰讵可待 + 1 大佬。大佬,膜拜了
Hmily + 1 + 10 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
ajdjcfdn + 1 + 1 return string.char((tonumber(h, 16) + 256 - 10 - key + 255999744) % 25
Small丶Lai + 1 + 1 如果没有包含关键字呢?
zy1234 + 1 + 1 谢谢@Thanks!
懒惰的上帝 + 2 + 1 挺简单的
秒针 + 1 + 1 我很赞同!
dibh10 + 1 + 1 用心讨论,共获提升!
lookerJ + 1 + 1 谢谢@Thanks!
小生我怕怕 + 10 我很赞同!
wmsuper + 2 + 1 我很赞同!
【=筱筱=】 + 3 + 1 热心回复!
╰Tang + 1 用心讨论,共获提升!
小俊 + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

3448797081 发表于 2018-5-23 22:02
https://pan.baidu.com/s/1Rfm96a_nfIdDGFmBcMvxOw

点评

密码在文件中,代码和数据也在文件中,你自己真的尝试过吗?  发表于 2018-5-25 01:43

免费评分

参与人数 1吾爱币 -1 收起 理由
Ganlv -1 论坛禁止求脱求破,求助软件分析思路,务必在主题帖中描述清楚你的分析思路.

查看全部评分

洛丹伦的夏天 发表于 2018-5-20 10:11
我发现,这样穷举密钥找关键字会有问题,如果作者是几层加密,里层故意放部分不加密代码,这样子会有问题。总之还是有key源码来的快,直接return 后面的值赋给一个字符串然后写入到其他文件里

免费评分

参与人数 1热心值 +1 收起 理由
Ganlv + 1 其实这类脚本的作者每次只是更新个时间限制,具体内容根本没什么变化

查看全部评分

dk2008 发表于 2018-5-20 20:23
mmEXP 发表于 2018-5-19 17:39
先占位,在慢看!
xuwenyi 发表于 2018-5-19 17:55
先占位,在慢看!
tzzhj1 发表于 2018-5-19 18:05
躺下慢慢看
骚碟。 发表于 2018-5-19 18:08
这个不错!!支持
kingwl 发表于 2018-5-19 18:19
        感谢发布原创作品,吾爱破解论坛因你更精彩!
clf3211147 发表于 2018-5-19 18:37
看不懂lua,感谢分享,支持一下
吞茶嚼花 发表于 2018-5-19 19:13
是噢……256取余数……范围只有0-256 我给忘了。。
头像被屏蔽
w5645060 发表于 2018-5-19 19:17
提示: 作者被禁止或删除 内容自动屏蔽
17602053348 发表于 2018-5-19 19:19
用一句话总结一下上面我们分析得到
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-17 00:51

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表