记一次对vm保护的算法的快速定位

admin 2022年11月1日22:33:10程序逆向评论18 views14922字阅读49分44秒阅读模式

记一次对vm保护的算法的快速定位

本文为看雪论坛精华文章

看雪论坛作者ID:lxcoder


在找sgmain相关资料时看到了这个贴子:https://bbs.pediy.com/thread-267741.htm
之后对大佬使用的trace工具很感兴趣,对反编译引擎也很感兴趣,不过底蕴不够,反编译引擎之类的也就将就用第三方,只能常试用frida的Stalker改了个类似的trace工具试了下效果也就有了这篇文章。

用到的工具:ida/Ghidra/frida


看的是手淘6.5.82版本的sgmain,因为本身只是对trace工具的一次验证,所以不包含实际的算法分析过程,只谈测试过程中的感受。

快速定位主要依赖的是Stalker,ida和Ghidra主要是用于静态分析。
在静态分析上,我这种小菜鸡就不讨论两个反编译引擎的本质哪个优劣,就谈使用的感受。

先上对比,对于sgmain的doCommand方法。


ida的效果:

记一次对vm保护的算法的快速定位
ghidra的效果:
记一次对vm保护的算法的快速定位
源码大概是这样子的:

__int64 __fastcall sub_2DB50(JNIEnv *param_1,_int64 param_2,int param_3,long param_4){    unsigned int v12 = 0;    long ret = 0;    long* ptr = (long*)malloc(0x20);    if(ptr != nullptr){        ptr[0] = 0LL;        ptr[1] = 0LL;        *(_DWORD *)ptr = param_3 / 10000;        v7 = param_3 % 10000 / 100;        *((_DWORD *)ptr + 1) = v7;        *((_DWORD *)ptr + 2) = param_3 % 100;        ptr[2] = param_1;        ptr[3] = param_4;        ret = sub_2BB3C(param_3 / 10000, v7, param_3 % 100, 1u, ptr, &v12);    }    free(ptr);     if(ret == 0 && v12 != 0){        //throw Exception        sub_E78C0(param_1, v12, 0x20aa97)    }    return ret;}

可以很明显看出ida对除法的优化更好一些,ghidra在除法这块就没有ida好,但是我个人更喜欢Ghidra的效果。

这里说句题外话,关于除法,编译器对一个整数进行除法运算时往往会转换成乘一个魔数然后除2的多少次方这种,这个推导过程很复杂,本质上就是把除任意整数变成除2的N次方的数,但过程中存在很多边界问题导致比较复杂,不过如果当ida之类的返编译器在复杂需要手动推导时,网上有种方法可以算出大概的值,不过涉及到符号问题及取模问题,记得好像是有Sub类指令参与的可能就是取模,但没有细究,有兴趣的可以自己编译后对比看看。

一般像这种同时存在
乘法、位移0x3F/0x1f取符号位、ASR之类指令时,基本八九不离十就可能是除法。
LOAD:000000000002DBFC A9 75 91 52+                MOV             W9, #0x68DB8BADLOAD:000000000002DBFC 69 1B AD 72LOAD:000000000002DC04 A9 7E 29 9B                 SMULL           X9, W21, W9LOAD:000000000002DC08 1F 7D 00 A9                 STP             XZR, XZR, [X8]LOAD:000000000002DC0C E8 1B 40 F9                 LDR             X8, [SP,#0x80+p]LOAD:000000000002DC10 2C FD 7F D3                 LSR             X12, X9, #0x3F ; '?'LOAD:000000000002DC14 29 FD 6C 93                 ASR             X9, X9, #0x2C ; ','LOAD:000000000002DC18 20 01 0C 0B                 ADD             W0, W9, W12LOAD:000000000002DC1C 0A E2 84 52                 MOV             W10, #0x2710LOAD:000000000002DC20 EB A3 90 52                 MOV             W11, #0x851FLOAD:000000000002DC24 00 01 00 B9                 STR             W0, [X8]LOAD:000000000002DC28 6B 3D AA 72                 MOVK            W11, #0x51EB,LSL#16LOAD:000000000002DC2C 0A D4 0A 1B                 MSUB            W10, W0, W10, W21LOAD:000000000002DC30 E8 1B 40 F9                 LDR             X8, [SP,#0x80+p]LOAD:000000000002DC34 4A 7D 2B 9B                 SMULL           X10, W10, W11LOAD:000000000002DC38 A9 7E 2B 9B                 SMULL           X9, W21, W11LOAD:000000000002DC3C 4B FD 7F D3                 LSR             X11, X10, #0x3F ; '?'LOAD:000000000002DC40 4A FD 65 93                 ASR             X10, X10, #0x25 ; '%'LOAD:000000000002DC44 41 01 0B 0B                 ADD             W1, W10, W11LOAD:000000000002DC48 01 05 00 B9                 STR             W1, [X8,#4]LOAD:000000000002DC4C 2A FD 7F D3                 LSR             X10, X9, #0x3F ; '?'LOAD:000000000002DC50 28 FD 65 93                 ASR             X8, X9, #0x25 ; '%'LOAD:000000000002DC54 E9 1B 40 F9                 LDR             X9, [SP,#0x80+p]LOAD:000000000002DC58 08 01 0A 0B                 ADD             W8, W8, W10LOAD:000000000002DC5C 8A 0C 80 52                 MOV             W10, #0x64 ; 'd'LOAD:000000000002DC60 02 D5 0A 1B                 MSUB            W2, W8, W10, W21LOAD:000000000002DC64 22 09 00 B9                 STR             W2, [X9,#8]

如上面的这种代码,就可以尝试用(1 << XX) / (magicNum - 1)这种方式来算出大概的除数,比如就上面的指令来说:
>>> (1 << 0x2c) / (0x68db8bad - 1)10000.000002510205>>> (1 << 0x25) / (0x51eb85ef - 1)99.99998491839733

而且在patch后的及时效果上面感觉更好一些,特别是那些br reg,adrp ret之类的跳转识别效果会比ida好,用ida的时候那个JMPOUT很尴尬,即使你使用跳转patch了也不一定能有效f5,可能还得去除中间的一些混淆的乱码才可以。

其次Ghidra也存在一个比较尴尬的问题,就像上面这个代码里面框起来的代码,那个0x2bb3c这个函数(ghidra会自动添加基址,默认是0x100000),在初次打开时,这个函数被识别为了空函数,而且无反回,这也就直接导致下面的那个0xe78c0所在的代码块被直接隐藏了,因为lStack76 != 0被直接视为了不可达分支,修改函数定义后代码也就立刻重新生成了上面的这个效果,修改是及时生效的,效果不错。

现在的一些间接跳转的混淆,不管他怎么变形,实际上很多时候都是常量表达式,而对于常量表达式的这种间接跳有很多种方式处理。

比如0x2bb3c头部的这段代码:
LOAD:000000000002BB3C                 SUB             SP, SP, #0x1B0LOAD:000000000002BB40                 STP             X28, X27, [SP,#0x1A0+var_50]LOAD:000000000002BB44                 STP             X26, X25, [SP,#0x1A0+var_40]LOAD:000000000002BB48                 STP             X24, X23, [SP,#0x1A0+var_30]LOAD:000000000002BB4C                 STP             X22, X21, [SP,#0x1A0+var_20]LOAD:000000000002BB50                 STP             X20, X19, [SP,#0x1A0+var_10]LOAD:000000000002BB54                 STP             X29, X30, [SP,#0x1A0+var_s0]LOAD:000000000002BB58                 ADD             X29, SP, #0x1A0LOAD:000000000002BB5C                 MRS             X26, #3, c13, c0, #2LOAD:000000000002BB60                 MOV             W23, W1LOAD:000000000002BB64                 MOV             W24, W0LOAD:000000000002BB68                 MOV             W10, #0x2710LOAD:000000000002BB6C                 MOV             W12, #0x64 ; 'd'LOAD:000000000002BB70                 LDR             X14, [X26,#0x28]LOAD:000000000002BB74                 MUL             W15, W24, W10LOAD:000000000002BB78                 MUL             W12, W23, W12LOAD:000000000002BB7C                 MOV             W22, W2LOAD:000000000002BB80                 MOV             W13, #0xC600LOAD:000000000002BB84                 ADD             W10, W15, W12LOAD:000000000002BB88                 MOV             W8, #0x96LOAD:000000000002BB8C                 SUB             X9, X29, #-var_70LOAD:000000000002BB90                 MOVK            W13, #0x53E,LSL#16LOAD:000000000002BB94                 ADD             W10, W10, W22LOAD:000000000002BB98                 MOV             X21, X4LOAD:000000000002BB9C                 ADD             W13, W10, W13LOAD:000000000002BBA0                 STUR            X14, [X29,#var_58]LOAD:000000000002BBA4                 STUR            W8, [X29,#var_70]LOAD:000000000002BBA8                 ADR             X27, loc_2BBACLOAD:000000000002BBACLOAD:000000000002BBAC loc_2BBAC                               ; DATA XREF: sub_2BB3C+6C↑oLOAD:000000000002BBAC                 LDRSW           X4, =0xFFFFFE62LOAD:000000000002BBB0                 MOV             X20, #0xABLOAD:000000000002BBB4                 EOR             X4, X4, X20LOAD:000000000002BBB8                 ADD             X4, X4, #0x88LOAD:000000000002BBBC                 MVN             X4, X4LOAD:000000000002BBC0                 LDRSW           X10, [X9]LOAD:000000000002BBC4                 EOR             X4, X4, X10LOAD:000000000002BBC8                 ADD             X27, X27, X4LOAD:000000000002BBCC                 MOV             X8, #0x62 ; 'b'LOAD:000000000002BBD0                 BR               X27
代码看起来有点复杂,各种运算,还有间接地址访问,而且每个函数头部都有这种间接跳,且貌似每个表达式都不一样,但实际上这种处理起来也相当简单,分静态和动态两种情况。

动态的情况不用说,单步或trace来获取对应的跳转地址即可,对于Ghidra,也不需要特别处理,它会自动帮你把这些常量表达式计算出来,在进行反编译时会自动使用它计算出的结果进行代入。

比如下面的是Ghidra的汇编显示:
                     LAB_0013dd1c                                    XREF[1]:     0013dd00(j) 0013dd1c e8 03 00 90     adrp       x8,0x1b90000013dd20 08 01 11 91     add        x8,x8,#0x4400013dd24 08 09 40 f9     ldr        x8,[x8, #0x10]=>DAT_001b9450                     = 000000000013DC94h0013dd28 08 71 02 91     add        x8,x8,#0x9c0013dd2c 00 01 1f d6     br         x8=>LAB_0013dd30

而如果你要说就用ida,不开启动态调试的情况下也简单,用ida的脚本使用unicorn进行模拟执行一下结果就跑出来了:
记一次对vm保护的算法的快速定位
对于那种代码之间被各种跳转包围的间接跳,例如:ida中那种函数中间的间接跳。
LOAD:000000000002BD58             loc_2BD58                               ; DATA XREF: sub_2BB3C+3B0↓oLOAD:000000000002BD58                                                     ; LOAD:off_16FC38↓oLOAD:000000000002BD58 E9 23 0A A9                 STP             X9, X8, [SP,#0x1A0+var_100]LOAD:000000000002BD5C 88 36 40 F9                 LDR             X8, [X20,#(off_16FC28 - 0x16FBC0)] ; loc_2BD68LOAD:000000000002BD60 FF 0F 00 F9                 STR             XZR, [SP,#0x1A0+var_188]LOAD:000000000002BD64 08 2D 00 91                 ADD             X8, X8, #0xBLOAD:000000000002BD68LOAD:000000000002BD68             loc_2BD68                               ; DATA XREF: sub_2BB3C+220↑oLOAD:000000000002BD68                                                     ; LOAD:off_16FC28↓oLOAD:000000000002BD68 03 00 00 14                 B               loc_2BD74 ; loc_2BD74 ; Keypatch modified this from:LOAD:000000000002BD68                                                     ;   BR X8

ghidra函数中部的间接跳:
                     LAB_0013dd1c                                    XREF[1]:     0013dd00(j) 0013dd1c e8 03 00 90     adrp       x8,0x1b90000013dd20 08 01 11 91     add        x8,x8,#0x4400013dd24 08 09 40 f9     ldr        x8,[x8, #0x10]=>DAT_001b9450                     = 000000000013DC94h0013dd28 08 71 02 91     add        x8,x8,#0x9c0013dd2c 00 01 1f d6     br         x8=>LAB_0013dd30

处理方式其实差不多,不管是ida还是Ghidra它们即便是能算出来跳转地址,也会给你提示,比如上面的ida就提示地址是loc_2BD68,但是他们为了确宝流程尽可能准确,所以一般不会在返汇编窗口中进行常量化,这时候就需要进行手动patch。

如果是硬是要ida配合Unicorn,这时候也不是不可以,但是因为部分常量在其它代码块中,再加上里面经常混杂一些地址中常量的访问,在通用性上面可能会不太好,所以这种时候一般建议是用trace的数据,或依赖现有工具返汇编引擎的结果来参考进行Patch。

比如,在Ghidra中与上面的类似的代码块在反汇编引擎中一般会给你提示出来:

记一次对vm保护的算法的快速定位
在汇编页面就是如下的这种:
                     LAB_00134638                                    XREF[1]:     00134604(j) 00134638 69 22 40 a9     ldp        x9,x8,[x19]0013463c a8 03 18 f8     stur       x8,[x29, #local_90]00134640 08 7d 40 92     and        x8,x8,#0xffffffff00134644 a9 a7 38 a9     stp        x9,x9,[x29, #local_88]00134648 a8 c3 17 b8     stur       w8,[x29, #local_94]0013464c e9 07 00 b4     cbz        x9,LAB_0013474800134650 c8 07 00 34     cbz        w8,LAB_0013474800134654 f6 09 00 90     adrp       x22,0x27000000134658 d6 82 01 91     add        x22,x22,#0x600013465c c8 4a 40 f9     ldr        x8,[x22, #0x90]=>DAT_002700f0                    = 00000000001345FDh00134660 08 ad 01 91     add        x8,x8,#0x6b00134664 00 01 1f d6     br         x8=>LAB_00134668

可以看出,Ghidra已经通过自己的逻辑算出了常量地址,但是它并没有更新到返汇编结果中,patch后效果立马就出来了:
记一次对vm保护的算法的快速定位
这种是间接跳,使用了全局变量进行跳转(大致含义就是全局变量0x270140这个地址中的内容+0xda计算出的结果就是call的地址),Ghidra这里识别为多种结果所以这种情况必需得上动态才能知道可能的结果了,不同流程可能结果会不一样。


通过上面类似的反混淆操作后0x2bb3c这个函数大致的伪代码也就可以看了:
typedef struct _StData{    void ** ppParamSt;    int sz;}StData; typedef struct _StParam1{    int param;    int unknow;    StData* pSD;}StParam1; typedef struct _StParam2{    int unknow;    int param;    long flag;    StData* pSD;}StParam2; typedef struct _StParam3{    long unknow;    int param;    int unknow;    long encryFuncAddr;}StParam3;   long FUN_0012bb3c(int param_1, int param_2, int param_3, int param_4,                  long *param_5, int *param_6){    int iVar1;    long lVar2;    StParam3 *pSVar3;    long local_1a8;    StParam1 *pSStack416;    long lStack408;    StParam2 *pSStack400;    long lStack392;    int local_17c;    long local_178;    int local_7c;    long *plStack120;    int *piStack112;    long local_68;    StData **pplVar1;     local_7c = 1;    plStack120 = param_5;    piStack112 = param_6;    local_178 = FUN_00168480(param_1 * 10000 + param_2 * 100 + param_3 + 88000000,                            param_4, 0, &plStack120, &local_7c);    if (local_7c != 0) {        pplVar1 = (long **)0x279af0;    //全局变量非常量        if (param_4 != 0) {            pplVar1 = (long **)&DAT_00279ae8;   //全局变量非常量        }         iVar1 = (*pplVar1)->sz;        if(iVar1 < 1){            local_17c = 0x26b0;        }else{            for(local_1a8 = 0; local_1a8 < iVar1; local_1a8++){                pSStack416 = (StParam1*)(*pplVar1)->ppParamSt[local_1a8];                if(pSStack416->param == param_1){                    goto LAB_JMP1;                }            }            pSStack416 = 0;LAB_JMP1:            if(pSStack416 == 0){                local_17c = 0x26b0;            }else{                iVar1 = pSStack416->pSD->sz;                if(iVar1 < 1){                    local_17c = 0x26b1;                }else{                    for(lStack408 = 0; lStack408 < iVar1; lStack408++){                        pSStack400 = (StParam2*)pSStack416->pSD->ppParamSt[lStack408];                        if(pSStack400->param == param_2){                            goto LAB_JMP2;                        }                    }                    pSStack400 = 0;LAB_JMP2:                    if(pSStack400 == 0){                        local_17c = 0x26b1;                    }else{                        iVar1 = pSStack400->pSD->sz;                        if(iVar1 > 0){                            for(lStack392 = 0; lStack392 < iVar1; lStack392++){                                pSVar3 = (StParam3*)pSStack400->pSD->ppParamSt[lStack392];                                if(pSVar3 != 0 && pSVar3->param == param_3){                                    local_178 = (*(func*)(pSStack400->flag ^ pSVar3->encryFuncAddr))(param_5, param_6);                                    return local_178;                                }                            }                        }                        local_17c = 0x26b2;                    }                }            }         }         if (param_6 != (int *)0x0) {            *param_6 = local_17c + (param_2 * 100 + param_1 * 10000 + param_3) * 10000;        }        local_178 = 0;    }      return local_178;}

常见的混淆基本都是差不多的处理方式,再复杂的基本也离不开Trace了。
 
Stalker与ida的trace各有优缺点吧,ida的trace是依托于调试器的,所以调试器有的缺点他都有,一般配合DBG_Hooks脚本处理效果更好,我一般喜欢在定位范围缩小到一定程度后才会用它,再则是用它来追踪程序的退出流程返向推检测的代码,不过这里主要说Stalker使用的问题就不聊它了。

Stalker很早之前就存在了,不过貌似这个东西在arm64之后才用的比较多一些,在arm32里面需要处理跳转地址的问题,而我实际使用过程中发现,如果开启了call事件,基本必崩,所以所有事件都是false的情况下做的处理,简而言之就是只用了transform回调。

在使用过程中发现几点问题:
1、Stalker的模式给我的感觉是将每一个以b/ret之类的流程跳转指令作为块的分离点来处理的,transform入口处iterator.next()返回的是块的地址,所以在这里直接使用某一地址来判断是否为Trace跟踪的代码区间可能会存在问题,比如,我在尝试任意指令hook时不遍历整个iterator块而用addr.compare == 0作为判断就会永远无法到达,因为transform每次来时iterator.next()返回的只会是代码块的首地址;

2、由于前面的原因,所以iterator.putCallout中的指令是没有b/ret之类跳转指令的,在arm32里面常见的adrp pc, #imm这类指令在这里没有看到,所以暂时不清楚这种情况会不会有其它变化;

3、instruction的regsRead和regsWritern里面是空的,碰到跳转或条件指令里里面会有值,但不包含常规寄存器,猜测可能是在性能优化上的有意为之,不过问题不大,无非就是在处理ldr x0,[x0]这类指令时打印寄存器值时需要注意一些。

除了这些外貌似也没什么太大的问题,整体执行速度还不错,就是代码量大时偶尔会挂掉,特别是在跟踪步入bl指令时。

然后依拖于Stalker封装了几个函数:
//跟踪函数//param0:跟踪的模块//param1:激活trace的hook点,主要是做条件判定,符合条件的才trace//param2-param3:跟踪的代码块区间,主要用来控制trace的指令区间//param4:是否步入(超出模块的不跟),默认False,开True容易触发time校验导至挂掉// trace_entry("libsgmainso-6.5.82.so", base.add(0x2db50), 0x2bb3c, 0x2befc) //同上,跟踪所有bl指令,并打印参数信息,快束定位这个挺好使,这里测试是字符串,其它的可以做适当调整// trace_entry_bl("libsgmainso-6.5.82.so", base.add(0x2db50), 0x2db50, 0x2DCF0, true) //跟踪所有逻辑指令,比如lsl/lsr等,打印偏移/VA及详细指令信息,实际使用效果不好,干扰太多// trace_entry_logic("libsgmainso-6.5.82.so", base.add(0x2db50), 0x2db50, 0x2DCF0, true) //同上//param4:为在指定地址的指令执行时调用回调//param5:回调函数//函数功能主要是处理在特殊地址执行时相关数据的打印// trace_callback("libsgmainso-6.5.82.so", 0x2db50, 0x2bb3c, 0x2befc, 0x2bc84, function(context) {//     var insn = Instruction.parse(context.pc)//     var insnStr = insn.toString().padEnd(25, " ")//     var strLog = insn.address.sub(g_baseAddr) + "  " + insn.address + ":t" + insnStr + "t" + "  " + getOpInfo(context, insn)//     console.log(strLog)//// })

因为是随手测试的,所以没有专门去看sign的计算是哪一个,就随手测试了几个值,不需要的屏蔽掉了,也就有了下面的这个结果:
记一次对vm保护的算法的快速定位
sign计算的中间call都应该算是比较清晰吧。

中间的流程大概就是在0xbc708处bl 0xbe1ac,之后就是0xbc8e0处调用0xc709c,参数是1PaJH2i4Bu60DMq4zUogvFil9Bs=字符串,之后一连串的bl 0xc792c,之后会使用新的函数调用bl 0x2bb3c进行执行,之后还有:
                                                x0:0x7ec5a203e0, x1:0x1ec, x2:0x1ec, x3:0x0, x4:0x0, x5:0x0, x6:0x0, x7:0x7ea70754f40xe73ac  0x7ea70f43ac:  blr x8                          -->(libart.so!0x32d3a4  _ZN3art3JNI12NewByteArrayEP7_JNIEnvi)  x8:0x7ecf7133a4x0:0x7ec5a203e0, x1:0x49, x2:0x0, x3:0x1ec, x4:0x7e53030800, x5:0x7ea7a7b348, x6:0x0, x7:0x7ea70754f4                                                arg4:enxH+QwhGIL9C/JrXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXjm6o=0xe7414  0x7ea70f4414:  blr x8                          -->(libart.so!0x32d424  _ZN3art3JNI18SetByteArrayRegionEP7_JNIEnvP11_jbyteArrayiiPKa)  x8:0x7ecf713424

流程貌似还算比较清晰。

然后在ghidra里面看了一下,不忍直视,来点片段享受一下吧:

0xbc560
do {  uVar11 = puVar6[1];  uVar10 = *puVar6;  local_880 = CONCAT17((byte)((ulong)local_870 >> 0x38) ^                       (byte)((uVar10 & 0xffffff0000000000) >> 0x38),                       CONCAT16((byte)((ulong)local_870 >> 0x30) ^                                (byte)((uVar10 & 0xffffff0000000000) >> 0x30),                                CONCAT15((byte)((ulong)local_870 >> 0x28) ^                                         (byte)(uVar10 >> 0x28),                                         CONCAT14((byte)((ulong)local_870 >> 0x20) ^                                                  (byte)(uVar10 >> 0x20),                                                  CONCAT13((byte)((ulong)local_870                                                                 >> 0x18) ^                                                           (byte)(uVar10 >> 0x18),                                                           CONCAT12((byte)((ulong)                                  local_870 >> 0x10) ^ (byte)(uVar10 >> 0x10),                                  CONCAT11((byte)((ulong)local_870 >> 8) ^                                           (byte)(uVar10 >> 8),                                           (byte)local_870 ^ (byte)uVar10)))))));  uStack2168 = CONCAT17((byte)((ulong)uStack2152 >> 0x38) ^                        (byte)((uVar11 & 0xffffff0000000000) >> 0x38),                        CONCAT16((byte)((ulong)uStack2152 >> 0x30) ^                                 (byte)((uVar11 & 0xffffff0000000000) >> 0x30),                                 CONCAT15((byte)((ulong)uStack2152 >> 0x28) ^                                          (byte)(uVar11 >> 0x28),                                          CONCAT14((byte)((ulong)uStack2152 >> 0x20)                                                   ^ (byte)(uVar11 >> 0x20),                                                   CONCAT13((byte)((ulong)uStack2152                                                                  >> 0x18) ^                                                            (byte)(uVar11 >> 0x18),                                                            CONCAT12((byte)((ulong)                                  uStack2152 >> 0x10) ^ (byte)(uVar11 >> 0x10),                                  CONCAT11((byte)((ulong)uStack2152 >> 8) ^                                           (byte)(uVar11 >> 8),                                           (byte)uStack2152 ^ (byte)uVar11)))))));  FUN_001c792c(auStack592,puVar9,&local_880);  uStack2152 = puVar9[1];  local_870 = *puVar9;  puVar9 = puVar9 + 2;  uVar8 = uVar8 - 1;  puVar6 = puVar6 + 2;} while (uVar8 != 0);local_9a8 = local_9a8 & 0xfffffff0;

0xc709c
local_2a0 = param_1;puStack664 = param_2;do {  uVar19 = puStack664[1];  uVar16 = *puStack664;  local_2a8 = local_2a8 - 4;  uVar17 = (undefined)((ulong)uVar16 >> 0x20);  uVar8 = (ulong)CONCAT16((char)((ulong)uVar16 >> 0x28),                          (uint6)((uint)(CONCAT12(uVar17,(short)uVar16) & 0xff00ff) << 0x18 |                                 (uint)(byte)((ulong)uVar16 >> 8) << 0x10));  uVar9 = (ulong)CONCAT16((char)((ulong)uVar19 >> 0x28),                          (uint6)CONCAT11((char)uVar19,(char)((ulong)uVar19 >> 8)) << 0x10);  uVar15 = CONCAT15((char)((ulong)uVar16 >> 0x30),                    (uint5)CONCAT12((char)(uVar8 >> 0x18),                                    CONCAT11((char)(uVar8 >> 0x10),                                             (char)((ulong)uVar16 >> 0x10))) << 8);  uVar8 = (ulong)CONCAT16((char)(uVar8 >> 0x30),uVar15);  uVar18 = CONCAT15((char)((ulong)uVar19 >> 0x30),                    (uint5)CONCAT12((char)(uVar9 >> 0x18),                                    CONCAT11((char)(uVar9 >> 0x10),                                             (char)((ulong)uVar19 >> 0x10))) << 8);  uVar9 = (ulong)CONCAT16((char)(uVar9 >> 0x30),uVar18);  local_2a0[1] = ((ulong)CONCAT41((uint)(byte)((ulong)uVar19 >> 0x20) << 0x18,(char)uVar19) &                 0xffffffff00) << 0x18 |                 (ulong)CONCAT16((char)((uVar9 & 0xffffffff00000000) >> 0x30),                                 CONCAT15((char)((uVar9 & 0xffffffff00000000) >> 0x28),                                          CONCAT14((char)((ulong)uVar19 >> 0x38),                                                   CONCAT13((char)(uVar9 >> 0x18),
0xc792C
if ((param_1 != (uint *)0x0 && param_2 != (undefined *)0x0) && param_3 != (uint *)0x0) {  uVar9 = (param_3[3] & 0xff00ff00) >> 8 | (param_3[3] & 0xff00ff) << 8;  uVar9 = (uVar9 >> 0x10 | uVar9 << 0x10) ^ param_1[3];  local_d0 = param_1 + 0x24;  uVar10 = (*param_3 & 0xff00ff00) >> 8 | (*param_3 & 0xff00ff) << 8;  uVar10 = (uVar10 >> 0x10 | uVar10 << 0x10) ^ *param_1;  uVar11 = (param_3[1] & 0xff00ff00) >> 8 | (param_3[1] & 0xff00ff) << 8;  uVar11 = (uVar11 >> 0x10 | uVar11 << 0x10) ^ param_1[1];  uVar12 = (param_3[2] & 0xff00ff00) >> 8 | (param_3[2] & 0xff00ff) << 8;  uVar12 = (uVar12 >> 0x10 | uVar12 << 0x10) ^ param_1[2];  uVar14 = *(uint *)(&DAT_002bca7c + (ulong)(uVar10 >> 0x18) * 4) ^ param_1[4] ^           *(uint *)(&DAT_002bce7c + (ulong)(uVar11 >> 0x10 & 0xff) * 4) ^           *(uint *)(&DAT_002bd27c + (ulong)(uVar12 >> 8 & 0xff) * 4) ^           *(uint *)(&DAT_002bd67c + (ulong)(uVar9 & 0xff) * 4);  uVar1 = *(uint *)(&DAT_002bca7c + (ulong)(uVar12 >> 0x18) * 4) ^ param_1[6] ^          *(uint *)(&DAT_002bce7c + (ulong)(uVar9 >> 0x10 & 0xff) * 4) ^          *(uint *)(&DAT_002bd27c + (ulong)(uVar10 >> 8 & 0xff) * 4) ^          *(uint *)(&DAT_002bd67c + (ulong)(uVar11 & 0xff) * 4);  uVar2 = *(uint *)(&DAT_002bca7c + (ulong)(uVar11 >> 0x18) * 4) ^ param_1[5] ^          *(uint *)(&DAT_002bce7c + (ulong)(uVar12 >> 0x10 & 0xff) * 4) ^          *(uint *)(&DAT_002bd27c + (ulong)(uVar9 >> 8 & 0xff) * 4) ^          *(uint *)(&DAT_002bd67c + (ulong)(uVar10 & 0xff) * 4);  uVar9 = *(uint *)(&DAT_002bca7c + (ulong)(uVar9 >> 0x18) * 4) ^ param_1[7] ^          *(uint *)(&DAT_002bce7c + (ulong)(uVar10 >> 0x10 & 0xff) * 4) ^          *(uint *)(&DAT_002bd27c + (ulong)(uVar11 >> 8 & 0xff) * 4) ^          *(uint *)(&DAT_002bd67c + (ulong)(uVar12 & 0xff) * 4);

记一次对vm保护的算法的快速定位

- End -


记一次对vm保护的算法的快速定位


看雪ID:lxcoder

https://bbs.pediy.com/user-home-719948.htm

  *本文由看雪论坛 lxcoder 原创,转载请注明来自看雪社区。


记一次对vm保护的算法的快速定位

KCTF官网:https://ctf.pediy.com




# 往期推荐






记一次对vm保护的算法的快速定位


记一次对vm保护的算法的快速定位

球分享

记一次对vm保护的算法的快速定位

球点赞

记一次对vm保护的算法的快速定位

球在看



记一次对vm保护的算法的快速定位

点击“阅读原文”,了解更多!

原文始发于微信公众号(看雪学苑):记一次对vm保护的算法的快速定位

特别标注: 本站(CN-SEC.COM)所有文章仅供技术研究,若将其信息做其他用途,由用户承担全部法律及连带责任,本站不承担任何法律及连带责任,请遵守中华人民共和国安全法.
  • 我的微信
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年11月1日22:33:10
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                  记一次对vm保护的算法的快速定位 http://cn-sec.com/archives/1385036.html

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: