Mas0n
mailto:MasonShi@88.com
翻车鱼

[VNCTF 2021] ReWp及复现

这次比赛算是我第一个比赛 ,re拿了个12名,总排名28,不是很理想,先上wp

notsudoku

拿到手的是个exe,一嫖图标盲猜python编译的。

用上pyinstxtractor脚本先dump成pyc,然而并没有什么pyc格式的文件,盲猜是没有扩展名的文件2,手动修复一下文件头,再用uncompyle6解密出来。

正点

一开始直接导出文件,一堆乱码,捣鼓了大半天,试着直接打印,看到了韩文( *·#/*··#*/*···

http://cdn.shi1011.cn/2021/03/f9db9ad4cf73ab6656edab41ae946210.png

直接复制出来放notepad++里处理一番,代码用了函数修饰器,比较难看,慢慢还原出来。最后拿到代码

import time, sys, hashlib

class deque:

    def __init__(self):
        self.data = []
        self.d2d = []
        self.erda = 65

    def main(self):
        print('welcome baby~ ', end='')
        print('input your flag~:', end='')
        self.strs = input()
        print('your input is:', end='')
        print(self.strs)
        print("let's check......", end='')
        time.sleep(0.5)
        self.d2d = [
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0]
        ]

        if len(self.strs) % 2 != 0:
            sys.exit()
        for i in self.strs:
            if ord(i) > 52 or ord(i) < 48:
                sys.exit()

        x = str(hashlib.new('md5', bytes((self.strs), encoding='utf8')).hexdigest())
        if x[:6] != 'e3a912':
            sys.exit()
        self.flag = x

        temp = 0
        for i in range(0, len(self.strs), 2):
            temp += 1
            a = int(self.strs[i])
            b = int(self.strs[(i + 1)])
            self.d2d[a][b] = temp

        if self.d2d[0][1] != 24 or self.d2d[4][3] != 2:
            sys.exit()
        if self.d2d[0][2] != 1 or self.d2d[2][3] != 20:
            sys.exit()
        if self.d2d[1][0] != 23 or self.d2d[3][4] != 3:
            sys.exit()


        for j in range(0, 5):
            temp = 0
            for i in range(5):
                temp += self.d2d[j][i]

            if temp != self.erda:
                sys.exit()

        for j in range(0, 5):
            temp = 0
            for i in range(5):
                temp += self.d2d[i][j]

            if temp != self.erda:
                sys.exit()

        print('Goodjob!', end='')

        print('The flag is vnctf{', end='')
        print((self.flag), end='')
        print('}', end='')
        sys.exit()


deq = deque()
deq.main()

看下来思路很清晰了,输入文本需要满足的条件如下

  • 输入的文本只能是0~4之间的数字文本
  • md5 hash的前6位是e3a912 (: 然并卵
  • 5*5的宫格,宫格指定位置等于相应值
[
            [0, 24, 1, 0, 0],
            [23, 0, 0, 0, 0],
            [0, 0, 0, 20, 0],
            [0, 0, 0, 0, 0],
            [0, 0, 0, 2, 0]
        ]
  • 行列和都是65

一股奥数的味道扑面而来……

关键

拉伊尔法制造五阶幻方

  1. 构造基方:用1-5填写幻方,使两对角线上分别是12345和33333,而且每横行纵行和均为15,如下
1 4 2 5 3
4 2 5 3 1
2 5 3 1 4
5 3 1 4 2
3 1 4 2 5

2. 然后构造根方:将基方左右调转过来,每个数都减去1,再乘以5,得到如下

10 20 5 15 0
0 10 20 5 15
15 0 10 20 5
5 15 0 10 20
20 5 15 0 10

3. 根方和基方每个对应小格中的数相加,得到最后幻方

11 24 7 20 3
4 12 25 8 16
17 5 13 21 9
10 18 1 14 22
23 6 19 2 15

然而结果不符合代码中的意思- o –

换种解法 – 参考

17  24  1   8   15
23  5   7   14  16
4   6   13  20  22
10  12  19  21  3
11  18  25  2   9

把1放在最上面的中间格,然后按口诀:

上面到顶了(出界)就往右移一格到最下面,也就是“2”的位置

住右上角移,如“3”位置。

右边到顶(出界)就往上移一格到最左边,也就是“4”的位置

然后依次往右上斜角移动,如右上方已经有数字了,就往本数向下移一格。例如“6”本应放在“5”的右上斜角,但这个右上斜角已经有数字“1”了,所以就往数字“5”本身向下移一格,就是“6”的位置

这个方法适用于所有奇数阶(9,25,49……宫格)幻方

一把梭

http://cdn.shi1011.cn/2021/03/b7656e1f5e7878ccb2807ec7888845f4.png
inputsStrs = ["0"] * 50
grid = [
    [17, 24, 1, 8, 15],
    [23, 5, 7, 14, 16],
    [4, 6, 13, 20, 22],
    [10, 12, 19, 21, 3],
    [11, 18, 25, 2, 9]
]

for i in range(0, len(grid)):
    for j in range(0, len(grid[i])):
        pos = 2 * grid[i][j] - 2
        inputsStrs[pos] = str(i)
        inputsStrs[pos + 1] = str(j)

print(''.join(inputsStrs))

MD5完事

http://cdn.shi1011.cn/2021/03/c45e3f72d9a10aa5910d9853b4ae6e58.png

Crackme2

无壳,反汇编,日常ndk分析,拿出libnative-lib.so

用IDA插件findcrypt一波搜

可能是Rijndael算法

http://cdn.shi1011.cn/2021/03/8f4f742569ac0a56998063e3eb3cc78e.png

用frida去hook

Java.perform(function () {
    var ptr = Module.findBaseAddress("libnative-lib.so");
    var method = ptr.add(0x042944)
    console.log(ptr);
    Interceptor.attach(method,{
            onEnter:function(arg){
                console.log("Hooking{libnative.so} arg[0] - " + arg[0]);
                console.log("Hooking{libnative.so String} arg[1] - " + Memory.readCString(arg[1]));
                console.log("Hooking{libnative.so length} arg[2] - " + arg[2]);
                console.log("Hooking{libnative.so AESkey} arg[3] - \n" + hexdump(arg[3],{length:16}));
                console.log("Hooking{libnative.so} arg[4] - " + arg[4].readInt());
            },
            onLeave:function(arg){
                console.log(hexdump(arg))
                console.log("Hooking{libnative.so} -  exit");
                var xmmword_C80F0 = ptr.add(0x0C80F0)
                console.log("xmmword_C80F0\n" + hexdump(xmmword_C80F0));
            }
        }
    )
})
http://cdn.shi1011.cn/2021/03/a3ac7ac391b057222190262ed5d405d3.png

解密完事

http://cdn.shi1011.cn/2021/03/8063bfee35e7f8e776384dcae244932e.png

十六进制明文,转换收工。

Ez_game

这个WEB题,感觉更像是JS逆向,从我re角度来说,翻看JS就能发现一段用sojsonv4混淆过的代码,此地无银三百两ovo

['sojson.v4']["\x66\x69\x6c\x74\x65\x72"]["\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72"](((['sojson.v4']+[])["\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72"]['\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65']['\x61\x70\x70\x6c\x79'](null,"118h97x114w32l95m95N101x110O99d111B100u101w32F61P32u39R106k115w106O105F97z109R105f46p99y111A109U39d44F10G32U32F32r32G95V97p32D61Y32X123W125h44r10M32V32P32x32h95X48M120O98J52A56I51A32r61d32v91o34Q95c100n101M99e111I100l101I34b44r32X34r104N116Z116m112B58A47b47P119M119d119M46z115W111I106V115f111s110m46J99D111W109Z47g106R97c118D97F115L99I114Y105F112v116E111l98m102Q117d115E99x97K116r111U114v46s104d116B109F108Z34e93B59q10m40r102O117f110Z99D116D105a111a110u40Y95J48O120H100Q54k52k50e120I49a41P32f123s10E32j32I32d32K95E48D120k100R54S52g50O120H49y91J95q48X120w98C52a56R51E91m48B93c93E32d61e32w95X48v120b98L52e56b51O91m49b93C10Q125V41N40t95r97g41L59X10M118d97I114t32E95f95x79y120f97D50O55p56y49X32I61j32A91K34i34t44h32L34a45m112w97p117H115M101E100z45m34r44g32Q34y87v105k110E33A32h102S108G97y103u123E116k104V105l115E95F103u97g109Y101I95l105R115A95x102G117h110l110E121M33B125f34N44c32q34f71C97L109O101x32c79k118R101J114R33b34L44V32h34U80g114e101b115c115t32c69w115I99g97u112W101z34C44h32d34a120f34k44B32W34i121H34q44x32P34E99x101x110k116J101Z114j34c44B32s34B117V110j100K101A102U105M110L101x100T34k44u32U34R108t111X103q34W44p32M34o21024U38500O34R44T32u34a29256O26412t21495E65292k106N115v20250y23450F34N44f32V34Q26399e24377C31383B65292R34u44t32j34y36824A35831C25903t25345D25105o20204W30340R24037P20316L34R44y32w34o106Z115A106T105F97b34i44a32m34u109T105L46e99p111F109l34U93m59Y10u108S101q116s32P98f105A103l84N101h120n116V32A61B32t95V95W79u120a97z50N55J56g49r91g48C120L48a93r59K10k105U102S32K40X112d97m117q115P101T100A41S32r123R10r32D32a32X32t98Y105Y103N84I101p120j116U32e61J32R95g95v79i120F97F50n55h56H49d91S48x120N49u93H10C125z59O10C105W102e32t40G119n105T110x84r105G109Y101W114q46C73s115m83G101O116j40m41O41R32S123R10c32I32b32A32V98x105X103v84E101b120V116V32L61P32N95e95u79I120v97W50T55W56i49S91j48m120O50d93k10g125J59G10Q105p102m32Y40O112J108s97c121w101U114u46P73a115D68E101Y97D100E40g41g41Q32Y123r10K32X32T32q32r98b105q103i84o101V120D116v32N61H32b95R95J79q120i97q50X55o56g49G91T48Z120F51G93l59k10c32v32f32e32P68u114l97N119q84f101x120z116m40j95r95I79n120R97E50x55o56h49y91p48Q120z52L93g44k32E109H97Q105E110w67Z97l110d118S97M115j83a105B122j101E91p95n95Y79U120S97H50C55a56l49E91z48w120A53C93Y93I32b47M32o50R44R32w109n97U105b110j67h97M110H118j97X115g83D105M122n101W91J95q95y79x120i97R50Y55z56s49N91z48v120R54s93a93S32I47z32A50k32w43S32f56j48j44T32B52Y50c41O10V125M59f10h68n114V97m119f84G101C120P116u40E98i105M103U84C101e120d116q44f32Z109v97i105R110G67u97m110Q118e97n115K83G105V122M101j91Q95Y95y79M120f97G50k55i56C49c91x48j120R53F93a93L32b47p32Z50k44a32l109c97r105A110W67i97Y110t118H97y115J83H105C122r101R91a95L95s79a120d97M50G55H56k49O91m48p120R54y93d93J32x47y32c50c32Y45d32C56M48i44r32b55G50f44j32x95j95r79S120i97g50J55V56a49a91Y48f120w55c93M44M32s50p41D59x59h59g10r40L102e117n110g99S116k105D111t110Y40M95J48s120f53y49N57K55H120Y50z44F32z95B48b120t53z49A57D55X120n51H44w32o95P48t120g53n49M57G55n120T52U44D32B95A48B120Z53K49H57t55X120d53L44f32o95J48R120D53s49H57d55J120b54Y44w32U95u48g120J53Q49v57B55s120a55H41z32L123K10G32X32F32V32W95k48o120T53S49I57d55s120i55d32O61X32n95B95n79d120E97E50s55o56w49j91N48z120D56R93R59j10p32Q32s32e32H95F48O120Y53F49C57W55r120q53M32f61U32Y102R117F110N99c116V105r111y110w40I95X48h120s53Y49o57q55k120o56Q41f32S123S10R32C32B32t32f32r32q32F32Z105Y102S32o40c116E121R112d101G111f102m32p97d108Q101M114s116V32G33s61C61z32B95p48W120e53S49y57p55Q120C55o41q32L123z10I32u32a32y32W32e32b32h32W32s32m32l32c97I108C101N114t116V40P95w48X120x53F49M57d55B120x56u41F10C32F32A32B32h32F32r32y32w125L59G10o32f32T32O32T32O32c32H32D105d102v32H40i116V121a112R101e111G102B32D99O111n110L115o111X108W101S32D33C61H61G32o95u48e120z53k49d57j55s120E55m41p32c123o10V32I32B32R32A32Y32N32H32E32R32l32V32P99D111R110i115r111m108h101D91Z95i95s79g120K97d50W55w56r49n91E48l120W57W93F93R40P95D48G120q53Q49d57p55q120M56R41F10q32C32N32u32X32T32E32d32X125W10D32P32h32i32Y125N59D10v32J32C32X32B95V48w120K53C49I57r55I120E52C32Q61E32X102o117o110I99u116M105C111z110h40v95g48L120b53d49q57e55J120L57j44C32F95R48X120M53S49U57w55h120I50w41W32r123j10q32e32k32e32y32Z32I32g32z114c101D116A117A114V110w32x95p48H120k53O49r57b55Z120w57Q32N43M32A95I48z120M53A49S57s55K120o50o10u32y32k32j32u125a59j10u32S32S32n32p95A48s120k53a49M57F55J120J54G32c61R32l95c48X120C53s49q57U55V120E52N40A95t95T79x120Q97R50o55F56e49Y91i48G120p97P93y44S32J95k48r120q53g49i57E55x120D52O40N95H48S120d53i49M57H55x120e52e40X95k95Z79q120C97l50T55K56I49k91R48o120g98g93Q44v32r95R95K79W120N97u50A55v56Z49m91A48x120X99H93x41y44T32w95n95v79i120g97E50y55h56m49C91X48S120N100w93n41q41b59M10I32e32F32g32e116J114t121I32r123N10M32J32N32m32j32l32J32g32X95n48g120l53X49b57l55D120p50s32b61T32f95W95E101N110o99c111M100M101Q59f10J32k32y32K32B32F32X32R32K105d102c32b40e33U40o116q121j112D101A111D102c32x95p48o120R53a49b57S55d120M50d32p33q61s61o32d95z48B120A53s49S57t55a120j55H32B38S38e32N95i48L120O53j49W57O55V120P50X32o61Y61i61k32P95j48M120Z53O49t57W55P120p52V40d95N95y79a120X97r50t55o56v49a91I48m120m101p93B44T32V95b95j79s120M97q50p55j56G49A91g48i120O102U93G41y41a41i32F123F10O32Z32o32T32P32e32Z32K32T32h32z32d32b95l48V120H53v49J57I55L120w53J40n95M48o120F53C49d57W55e120S54R41U10U32u32C32c32x32z32y32J32F125L10H32K32G32r32d125e32A99A97l116x99y104R32H40B101U41W32j123A10m32t32b32L32A32A32B32D32Z95t48z120m53V49N57N55B120x53Z40m95k48c120A53L49M57V55R120l54U41X10E32R32G32Q32q125U10D125p41P40A123E125S41"['\x73\x70\x6c\x69\x74'](/[a-zA-Z]{1,}/))))('sojson.v4');

一般遇到混淆的JS如果代码量不多,可以手动去还原,如果代码量上达几千几万,那就需要使用AST进行处理了。这里的混淆不是很难,能够轻易的还原

['sojson.v4'].filter.constructor(((['sojson.v4'] + []).constructor.fromCharCode.apply(null, "118h97x114w32l95m95N101x110O99d111B100u101w32F61P32u39R106k115w106O105F97z109R105f46p99y111A109U39d44F10G32U32F32r32G95V97p32D61Y32X123W125h44r10M32V32P32x32h95X48M120O98J52A56I51A32r61d32v91o34Q95c100n101M99e111I100l101I34b44r32X34r104N116Z116m112B58A47b47P119M119d119M46z115W111I106V115f111s110m46J99D111W109Z47g106R97c118D97F115L99I114Y105F112v116E111l98m102Q117d115E99x97K116r111U114v46s104d116B109F108Z34e93B59q10m40r102O117f110Z99D116D105a111a110u40Y95J48O120H100Q54k52k50e120I49a41P32f123s10E32j32I32d32K95E48D120k100R54S52g50O120H49y91J95q48X120w98C52a56R51E91m48B93c93E32d61e32w95X48v120b98L52e56b51O91m49b93C10Q125V41N40t95r97g41L59X10M118d97I114t32E95f95x79y120f97D50O55p56y49X32I61j32A91K34i34t44h32L34a45m112w97p117H115M101E100z45m34r44g32Q34y87v105k110E33A32h102S108G97y103u123E116k104V105l115E95F103u97g109Y101I95l105R115A95x102G117h110l110E121M33B125f34N44c32q34f71C97L109O101x32c79k118R101J114R33b34L44V32h34U80g114e101b115c115t32c69w115I99g97u112W101z34C44h32d34a120f34k44B32W34i121H34q44x32P34E99x101x110k116J101Z114j34c44B32s34B117V110j100K101A102U105M110L101x100T34k44u32U34R108t111X103q34W44p32M34o21024U38500O34R44T32u34a29256O26412t21495E65292k106N115v20250y23450F34N44f32V34Q26399e24377C31383B65292R34u44t32j34y36824A35831C25903t25345D25105o20204W30340R24037P20316L34R44y32w34o106Z115A106T105F97b34i44a32m34u109T105L46e99p111F109l34U93m59Y10u108S101q116s32P98f105A103l84N101h120n116V32A61B32t95V95W79u120a97z50N55J56g49r91g48C120L48a93r59K10k105U102S32K40X112d97m117q115P101T100A41S32r123R10r32D32a32X32t98Y105Y103N84I101p120j116U32e61J32R95g95v79i120F97F50n55h56H49d91S48x120N49u93H10C125z59O10C105W102e32t40G119n105T110x84r105G109Y101W114q46C73s115m83G101O116j40m41O41R32S123R10c32I32b32A32V98x105X103v84E101b120V116V32L61P32N95e95u79I120v97W50T55W56i49S91j48m120O50d93k10g125J59G10Q105p102m32Y40O112J108s97c121w101U114u46P73a115D68E101Y97D100E40g41g41Q32Y123r10K32X32T32q32r98b105q103i84o101V120D116v32N61H32b95R95J79q120i97q50X55o56g49G91T48Z120F51G93l59k10c32v32f32e32P68u114l97N119q84f101x120z116m40j95r95I79n120R97E50x55o56h49y91p48Q120z52L93g44k32E109H97Q105E110w67Z97l110d118S97M115j83a105B122j101E91p95n95Y79U120S97H50C55a56l49E91z48w120A53C93Y93I32b47M32o50R44R32w109n97U105b110j67h97M110H118j97X115g83D105M122n101W91J95q95y79x120i97R50Y55z56s49N91z48v120R54s93a93S32I47z32A50k32w43S32f56j48j44T32B52Y50c41O10V125M59f10h68n114V97m119f84G101C120P116u40E98i105M103U84C101e120d116q44f32Z109v97i105R110G67u97m110Q118e97n115K83G105V122M101j91Q95Y95y79M120f97G50k55i56C49c91x48j120R53F93a93L32b47p32Z50k44a32l109c97r105A110W67i97Y110t118H97y115J83H105C122r101R91a95L95s79a120d97M50G55H56k49O91m48p120R54y93d93J32x47y32c50c32Y45d32C56M48i44r32b55G50f44j32x95j95r79S120i97g50J55V56a49a91Y48f120w55c93M44M32s50p41D59x59h59g10r40L102e117n110g99S116k105D111t110Y40M95J48s120f53y49N57K55H120Y50z44F32z95B48b120t53z49A57D55X120n51H44w32o95P48t120g53n49M57G55n120T52U44D32B95A48B120Z53K49H57t55X120d53L44f32o95J48R120D53s49H57d55J120b54Y44w32U95u48g120J53Q49v57B55s120a55H41z32L123K10G32X32F32V32W95k48o120T53S49I57d55s120i55d32O61X32n95B95n79d120E97E50s55o56w49j91N48z120D56R93R59j10p32Q32s32e32H95F48O120Y53F49C57W55r120q53M32f61U32Y102R117F110N99c116V105r111y110w40I95X48h120s53Y49o57q55k120o56Q41f32S123S10R32C32B32t32f32r32q32F32Z105Y102S32o40c116E121R112d101G111f102m32p97d108Q101M114s116V32G33s61C61z32B95p48W120e53S49y57p55Q120C55o41q32L123z10I32u32a32y32W32e32b32h32W32s32m32l32c97I108C101N114t116V40P95w48X120x53F49M57d55B120x56u41F10C32F32A32B32h32F32r32y32w125L59G10o32f32T32O32T32O32c32H32D105d102v32H40i116V121a112R101e111G102B32D99O111n110L115o111X108W101S32D33C61H61G32o95u48e120z53k49d57j55s120E55m41p32c123o10V32I32B32R32A32Y32N32H32E32R32l32V32P99D111R110i115r111m108h101D91Z95i95s79g120K97d50W55w56r49n91E48l120W57W93F93R40P95D48G120q53Q49d57p55q120M56R41F10q32C32N32u32X32T32E32d32X125W10D32P32h32i32Y125N59D10v32J32C32X32B95V48w120K53C49I57r55I120E52C32Q61E32X102o117o110I99u116M105C111z110h40v95g48L120b53d49q57e55J120L57j44C32F95R48X120M53S49U57w55h120I50w41W32r123j10q32e32k32e32y32Z32I32g32z114c101D116A117A114V110w32x95p48H120k53O49r57b55Z120w57Q32N43M32A95I48z120M53A49S57s55K120o50o10u32y32k32j32u125a59j10u32S32S32n32p95A48s120k53a49M57F55J120J54G32c61R32l95c48X120C53s49q57U55V120E52N40A95t95T79x120Q97R50o55F56e49Y91i48G120p97P93y44S32J95k48r120q53g49i57E55x120D52O40N95H48S120d53i49M57H55x120e52e40X95k95Z79q120C97l50T55K56I49k91R48o120g98g93Q44v32r95R95K79W120N97u50A55v56Z49m91A48x120X99H93x41y44T32w95n95v79i120g97E50y55h56m49C91X48S120N100w93n41q41b59M10I32e32F32g32e116J114t121I32r123N10M32J32N32m32j32l32J32g32X95n48g120l53X49b57l55D120p50s32b61T32f95W95E101N110o99c111M100M101Q59f10J32k32y32K32B32F32X32R32K105d102c32b40e33U40o116q121j112D101A111D102c32x95p48o120R53a49b57S55d120M50d32p33q61s61o32d95z48B120A53s49S57t55a120j55H32B38S38e32N95i48L120O53j49W57O55V120P50X32o61Y61i61k32P95j48M120Z53O49t57W55P120p52V40d95N95y79a120X97r50t55o56v49a91I48m120m101p93B44T32V95b95j79s120M97q50p55j56G49A91g48i120O102U93G41y41a41i32F123F10O32Z32o32T32P32e32Z32K32T32h32z32d32b95l48V120H53v49J57I55L120w53J40n95M48o120F53C49d57W55e120S54R41U10U32u32C32c32x32z32y32J32F125L10H32K32G32r32d125e32A99A97l116x99y104R32H40B101U41W32j123A10m32t32b32L32A32A32B32D32Z95t48z120m53V49N57N55B120x53Z40m95k48c120A53L49M57V55R120l54U41X10E32R32G32Q32q125U10D125p41P40A123E125S41".split(/[a-zA-Z]{1,}/))))('sojson.v4');

使用正则以字母分割,得到一个文本数字的数组,然后转换成明文。

很简单,将函数调用去掉后面的('sojson.v4'),就能拿到正常的JS代码。

http://cdn.shi1011.cn/2021/03/2a5d13e7dc89737770d1f4276754df51.png

显然,flag就藏在__Oxa2781数组中。

Crackme1复现

因为对主流加密算法只有一些粗浅的印象,并不了解其中的算法。在我看了wp后,去了解了一下RC4算法。

RC4

参考大佬的学习文章,借此题中的RC4算法进行学习。

S 盒,是一个 256 个字节大小的 char 类型数组,char S[256]

密钥 K,长度限定在 1-256 个字节

临时变量 T,大小也是 256 个字节的数组

输出的密钥流 k,大小与实际需要加密的内容一致。

RC4

加解密流程

  1. 通过密钥调度算法KSA初始化状态矢量S(S就是一个随机数发生器,称为S-box)
  2. 再通过伪随机数生成算法PRGA得到密钥流KeyStream
  3. 密钥流KeyStream与明文进行XOR运算得到密文,解密同理

回到此题,作者简单混淆了一下变量名,我进行了分析和还原

package com.ctf.crackme;

public class enc {
    private String RC4Key = "vnctftestkeyyyy";

    public native String oO000O0(String str);

    private static void RC4init(short[] s, String key, int keylen) {
        byte[] keyArray = key.getBytes();
        for (int i = 0; i < 256; i++) {
            s[i] = (short) i; // 顺序填充 sbox
        }
        int j = 0;
        for (int i2 = 0; i2 < 256; i2++) { // 打乱sbox
            j = ((s[i2] + j) + keyArray[i2 % keyArray.length]) % 256;
            short tmp = s[i2]; // 交换
            s[i2] = s[j];
            s[j] = tmp;
        }
    }

    private static void RC4crypt(short[] s, char[] Data, int Len) {
        int i = 0;
        int j = 0;
        for (int k = 0; k < Len; k++) {
            i = (i + 1) % 256;
            j = ((s[i] & 255) + j) % 256;
            short tmp = s[i]; // 交换
            s[i] = s[j];
            s[j] = tmp;
            Data[k] = (char) (Data[k] ^ s[((s[i] & 255) + (s[j] & 255)) % 256]); // 逐字异或
        }
    }

    public char[] encrypt(String Data) {
        String Data_1 = oO000O0(Data);
        short[] s = new short[256];
        RC4init(s, this.RC4Key, this.RC4Key.length());
        char[] data = Data_1.toCharArray();
        RC4crypt(s, data, data.length);
        return data;
    }
}

分析的流程中,发现首先调用了native函数,转为分析libnative-lib.so

使用findcrypt发现并不是标准算法,手动分析

__int64 __fastcall Java_com_ctf_crackme_o000O00000o0_oO000O0(JNIEnv *a1, __int64 a2, __int64 a3)
{
  __int64 v4; // [xsp+0h] [xbp-1F0h]
  unsigned int v5; // [xsp+Ch] [xbp-1E4h]
  __int64 v6; // [xsp+10h] [xbp-1E0h]
  const char *Data; // [xsp+38h] [xbp-1B8h]
  char v9[24]; // [xsp+58h] [xbp-198h] BYREF
  char v10[24]; // [xsp+70h] [xbp-180h] BYREF
  char maps[24]; // [xsp+88h] [xbp-168h] BYREF
  char rdata[24]; // [xsp+A0h] [xbp-150h] BYREF
  char v13[32]; // [xsp+B8h] [xbp-138h] BYREF
  char v14[256]; // [xsp+D8h] [xbp-118h] BYREF
  __int64 v15; // [xsp+1D8h] [xbp-18h]

  v15 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  Data = sub_2B9C4(a1, a3, 0LL);                // GetStringUTFChars
  sub_233F8((__int64)rdata, (__int64)Data);     // 手上没有Android7.1的调试机, 应该是copy
  sub_233F8((__int64)maps, (__int64)"AYZpq23IJrTFfghijklCDE1KLMmBdestU5678GHz0cuvwabN9VWXnoOPQRSxy4/+");// copy
  sub_25F2C((__int64)v10, (__int64)maps);
  sub_2BA04(v13, v10, 61LL);
  sub_23588(v10);
  v6 = sub_2B7AC(rdata);
  v5 = sub_2BA6C(rdata);                        // wp上说是取了数据长度
  sub_25F2C((__int64)v9, (__int64)maps);
  sub_40BEC(v13, v14, v6, v5, v9);              // base64
  sub_23588(v9);
  v4 = sub_2B774(a1, v14);
  sub_2BA90(v13);
  sub_23588(maps);
  sub_23588(rdata);
  _ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
  return v4;
}

然而由于手上没有7.1的设备来动态调试,并且受制于我卑微的分析能力,我无法细致的分析更多函数的作用,日后有机会补上

下面回到最开始的程序入口

public void onClick(View view) {
                if (Arrays.equals(new char[]{145, '5', 129, 199, 133, 'u', 18, 'v', 'k', 160, 167, '\n', 135, 't', 'd', 179, 237, 174, 'J', 'i', 27, 183, 128, '7', 20, 221, 144, 29, 171, 7, 181, 22, 'g', '^', '\t', 162, 6, 232, 253, 170, 158, 207, '9', 210, 173, 240, 'S', '@', 133, 192, 136, 227}, new o000O00000o0().oO000O0000OOo(input.getText().toString()))) {
                    Toast.makeText(MainActivity.this, "Congratulations", 0).show();
                } else {
                    Toast.makeText(MainActivity.this, "Sorry!!!", 0).show();
                }
            }

先RC4解密再Base64解密

http://cdn.shi1011.cn/2021/03/0ace4842f47270f726df88671f259ca2.png

这里我卡住了,对Base64算法还是不熟悉,一直卡在变异算法的异或

http://cdn.shi1011.cn/2021/03/e21c6e6bc010519438734abe037a7e05.png

v4对每个分片的第1,3个进行异或

这里感谢V&NCTF交流群中的一位师傅给了我一个思路,代码如下

import base64
str1 = bytearray(b"BHu7B3jx@XrPjCBGkHARkOcRAXVVj7L6jpfo@HU6@HkPjHVVjp@4")
for p, i in enumerate(str1):
    if p % 2 == 0:  # 每两位进行异或
        str1[p] ^= 39  # 这个值来源于本位的加密文本长度 base64 变种

newMaps = "AYZpq23IJrTFfghijklCDE1KLMmBdestU5678GHz0cuvwabN9VWXnoOPQRSxy4/+"
baseMaps = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

tstr = str1.decode().translate(str.maketrans(newMaps, baseMaps)).encode()  # 对应替换
print(base64.b64decode(tstr).decode())

这个代码是令我眼前一亮的一个方法:直接使用原始码表对应新码表替换密文,可以免于替换码表。

通过这道题目,我很清楚我必须去理解一些主流算法了。标准算法学习笔记也就是下一篇或者后续需要的了

FilpGame复现

显然我技术还是不到家,这道题我并没有能够解出,翻看了函数的主逻辑,一致搞不懂代码的思路,途中有尝试通过hint寻找突破点,然而半途放弃了。官方wp已经出了几天了,但是因为一些私事一直没时间复现。今天得空来复现,学习一下思路。

常规

常规操作,查壳放IDA里,查看伪代码。

sub_6C1020("Input: ", v16);
sub_6C1050("%s", (char)ArgList);
v3 = 0;
v18 = 0;

v4 = 0;// 0
v5 = 0;// 0
v19 = 0;// 0

dword_6C2140 = [0x00, 0x00, 0xFFFFFF, 1, 0]
dword_6C212C = [0xFFFFFFFF, 0x00, 0, 0, 1]


while (v3 < 214) { // 长度少于214
    v6 = v3;
    v8 = v6 & 1;

    if (v8) { // 隔两位取坐标 x, y
        if (ArgList[v3] - '0' > 9) {

            if (ArgList[v3] - 'A' > 25) {
                v5 = -1;
            } else {
                v5 = ArgList[v3] - 55; // 这里困惑了很久,发现原来只是A - F转换成10 - 15
            }

        } else {
            v5 = ArgList[v3] - '0';
        }
		
    } else {
		
        if (ArgList[v3] - '0' > 9) {

            if (ArgList[v3] - 'A' > 25) {
                break;
            }
            v4 = ArgList[v3] - 55;

        } else {

            v4 = ArgList[v3] - '0';

        }
        v19 = v4;


    }

    if (v4 > 0xF || v5 > 0xF) break; //坐标 x,y 大小需小于16
 
    if (v8) { // 坐标完整的时候进入
        if (dword_6C3018 >= (int)(v4 + 16 * v5)) break; // 0xFFFFFFFF
        // dword_6C3018 = v4 + 16 * v5;
        for (i = 0; i < 5; ++i) { // 这个for相当于点灯游戏中的5个点的变化

            v12 = v4 + dword_6C212C[i];
            v13 = v5 + dword_6C2140[i];

            if (v12 <= 0xF && v13 <= 0xF) word_6C301C[v13] ^= 1 << (15 - v12); // 位异或
        }
    }

    v3++;
}

	v14 = word_6C301C; // int16类型 状态压缩
    while ( *v14 == -1 ) { // word_6C301C变量中所有的值都变成-1(0xffff)

      if ( (int)++v14 >= (int)&unk_6C303C ){ // 标记读取
        sub_6C1020("right, vnctf{MD5(%s)}\n", (char)ArgList);
        return 0;
      }
	  
    }

总的看下来,其实解题的关键很清楚。然而之前没玩过点灯游戏,我表示比赛的时候我是一脸懵的。

现在,我找了一个在线网站来了解点灯游戏。

也就有了我上面的代码分析。

事实上,其实思路很清楚:

  • 一个16×16的宫格
  • 点指定次数的灯让word_6C301C内的所有数据都为-1

然而事实是难点就在于怎么点才能让其值全为-1……

难点

待补充……

本文链接:https://blog.shi1011.cn/ctf/1087
本文采用 CC BY-NC-SA 3.0 Unported 协议进行许可

Mas0n

文章作者

发表评论

textsms
account_circle
email

翻车鱼

[VNCTF 2021] ReWp及复现
这次比赛算是我第一个比赛 ,re拿了个12名,总排名28,不是很理想,先上wp notsudoku 拿到手的是个exe,一嫖图标盲猜python编译的。 用上pyinstxtractor脚本先dump成pyc,然而并…
扫描二维码继续阅读
2021-03-19