本帖最后由 我是用户 于 2014-5-9 02:06 编辑
前言:里面unknow的算法是RSA512,感谢H大的解答以及参与这个CM的各位大牛。解答之前发的是PDF,由于不方便,现转为帖子,70多张图片,我已经加到吐了,想看PDF的也可以下载附件,个人认为还是PDF舒服点!
1 启动流程分析 1.1简述 这个CM利用父子进程启动,即父进程通过CreateMutex创建一个hmily的互斥对象,然后创建子进程,子进程判断是否存在hmily这个互斥对象来识别自身,流程图如下: CreateMutex判断自身是父进程还是子进程
1.2 父进程启动分析 父进程只负责创建子进程,以及接收子进程的调试来判断子进程的初始化情况 ,子进程初始化完毕之后,父进程通过DebugSetProcessKillOnExit与DebugActiveProcessStop这两个API来退出对子进程的调试状态,然后结束自身。 获得自身路径,以DEBUG_PROCESS创建子进程 等待子进程初始化完毕,然后结束自身。
1.3 子进程启动分析 子进程的初始化函数中分两个部分。 第一部用于Hook ZwWriteFile,极大隐藏了算法。 第二部分用于解密附加数据,然后将其在内存中载入,这个附加数据其实是一个DLL,里面有一个名为LSG的导出函数,这个CM的算法就是在这个函数里。 HookZwWriteFile: 创建一个线程,线程地址为0x401BC0, 这个线程用于Hook ZwWriteFile以及创建下一个线程,用于处理附加数据。 在线程0x401BC0中,子进程通过GetModuleHandle得到ntdll.dll的句柄,再通过GetProcAddress得到ZwWriteFile的函数地址,将地址保存在0x5B409C中。 先调用了下线程同步函数,然后申请空间保存ZwWriteFile的头5个字节,进行HOOK,然后再创建一个线程地址为0x401B80的线程来处理附加数据。 最后等待父进程结束调试,结束此线程。 解密附加数据: 子进程首先将自身文件读入内存,定位到附加数据起始点,然后对自身PE头进行CRC32效验,利用这个效验值作为3DES的Key来解密附加数据,因此如果被脱壳,这个CRC32的值肯定不对,所以这里我们可以用原值进行PATCH。如果处理失败则恢复原有的Hook。 在LoadDLL这个CALL里,首先通过CreateFile-GetFileSize-ReadFile读入自身程序进缓冲区。 定位附加数据 对PE头进行CRC32计算,这个CRC32效验值后面会作为KEY来解密附加数据,所以我们脱壳后,CRC32效验值失败,这里我们PATCH一下原程序的CRC32值0xDFE963A6。 将CRC32值转换成小写字符串,作为Key对附加数据进行第一次解密 解密采用了微软的加密库Crypto,解密算法是3DES,解密数据是附加数据,大小为0x109B0。 这是第一次解密后的附加数据 解密完之后,申请空间,对附加数据进行第二次解密。 这是二次解密后的数据,可以看到MZ头,这是一个DLL文件,我们后面再说。 二次解密完成后,这个CM就相当于一个PELOADER,将这个DLL载入这个CM的地址空间,完成PE Loader的初始化工作。 首先先判断一下PE格式
然后取随机数,然后申请空间,空间大小是0x33000,作为这个DLL的基址,这就导致每次这个DLL加载的地址都不一样,为了分析,我们也可以Patch这个数值。 复制PE头 复制各个区段 对地址进行重定位 处理IAT(载入DLL) 处理IAT(得到函数地址) 处理IAT(保存API地址) 给区段置入属性 Eax的值为这个DLL的入口点,进入入口点初始化。 DLL初始化其实就是在内存里对这个DLL进行脱壳,这个DLL加了一个UPX的外壳。 我们往下翻,发现,这个DLL的真实入口是0x71A70BAE,减去基址0x71A70000,入口点偏移是10BAE,稍后我们可以把这个DLL提出来。 最后处理导出表 最后很重要的一部,将LSG函数地址存入Hook后的函数中。 这样附加数据就处理完毕了。
2 算法分析 之前所做的所有的一切都是为了隐藏这个函数的算法,现在就让我们来一步步的拨开它的面纱。 算法也分两部分,第一部分纯粹就是用来迷惑我们的,用于第二部分验证的跳板。
2.1算法第一部分分析 对User:360进行MD5加密,得到MD5User 对Key:55……进行MD5加密,得到MD5Key 连接MD5User+MD5Key 通过WideCharToMultiByte将User与Key转换成多字节 用sprintf_s连接User+Key,然后Xor 0x23进行加密 然后创建360.dat文件,将MD5User+MD5Key写入,触发我们之前的Hook ZwWriteFile,跳入执行。 后续就是将Label标志赋值Failed,创建线程假装注册按钮响应,其实,注册成功Label是在失败Lable的底层,我们用资源查看器可以看到,注册成功后ShowWindow。
2.2算法第二部分 WriteFile-ZwWriteFile钩子,直接调用401A40处的函数,这里直接用正确的Key. User: 360 Key:552b437637727039504c6d75516d6439566d2f7a366c49554b76787a7939333659512b676f423739522b6251417235634b56614a5132726d344259424b346245704c2b7176685845674363624831425264614c537a673d3d 创建线程进行算法验证
这里是调用原来的代码 压入注册信息,调用LSG函数 Xor 23进行解密 用分隔符取注册信息 将Key转成十六进制表示 U+Cv7rp9PLmuQmd9Vm/z6lIUKvxzy936YQ+goB79R+bQAr5cKVaJQ2rm4BYBK4bEpL+qvhXEgCcbH1BRdaLSzg==
Base64解密得 53E0AFEEBA7D3CB9AE42677D566FF3EA52142AFC73CBDDFA610FA0A01EFD47E6D002BE5C295689436AE6E016012B86C4A4BFAABE15C480271B1F505175A2D2CE 对上述再次进行解密,这个算法,应该是RSA或者RC,记为Unknow吧。解密后的数据为: E74B2AFD58EEBD98CA661D55F56FED567B9533643A387D13D91FA80C08B11F73AF80820D5C5E91216546EBCB368D08EEA3691B 对360进行MD5加密,加密后得: E7B24B11 2A44 FDD9 EE93 BDF9 98C6 CA0E 取奇数位连接,与上述解密结果进行比较 E7B24B11 2A44 FDD9 EE93 BDF9 98C6 CA0E取奇数位为 E74B2AFDEEBD98CA E74B2AFD58EEBD98CA661D55F56FED567B9533643A387D13D91FA80C08B11F73AF80820D5C5E91216546EBCB368D08EEA3691B取前四位,与第6-9位。 E74B2AFDEEBD98CA 两者进行比较 E74B2AFD58EEBD98CA661D55F56FED567B9533643A387D13D91FA80C08B11F73AF80820D5C5E91216546EBCB368D08EEA3691B取第9位至最后一位 1D55F56FED567B9533643A387D13D91FA80C08B11F73AF80820D5C5E91216546EBCB368D08EEA3691B进行3DE_112解密,密钥是lingdux
解密后的结果是 然后对Success进行MD5加密 加密后的结果与Success的MD5值比较 验证成功的话则显示成功信息
2.3注册机的编写。 因为有一个算法未知,所以这里只能说下流程。 先将User进行MD5计算,然后 取奇数位连接,再加上 1D55F56FED567B9533643A387D13D91FA80C08B11F73AF80820D5C5E91216546EBCB368D08EEA3691B,这样我们就可以构造Key1,将Key1用Unknow(未知算法)进行加密,然后再进行BASE64加密,将加密后的值转成字符串就得到了Key。
3.Patch注册码
3.1脱壳,修复附加数据 脱壳用ESP定律,或者直接往下翻就行,这里要注意的是附加数据的处理。用overlay将附加数据复制出来 然后将附加数据用C32追加到到脱壳文件尾中。 处理完之后再作相应的Patch 先Patch父进程 将401C08 Nop掉 再Patch Crc32值 Patch注册信息 我们选取在地址是,Xor 23加密处,我们可以直接提取已加密好的注册信息,然后找一个空白地址,将它保存。 注册信息的地址是0x5832C5 Patch原地址代码 Patch后的地址代码 保存一下,然后输入任意信息都能注册了。 当然也可以Patch DLL生成的地址,使它地址固定,然后写个SMC来PATCH,总之方法多多了。 关于提取DLL要注意时机,不要在他初始化完之后再提取。然后将这个DLL丢到IDA里分析,不然OD不支持MMX指令集,有些代码看起来心烦。
4.结语 基本上这个CM就这些东西了,说个小BUG,在itoa函数转换MD5码时,如果MD5码中有一字节是0A格式的话,转换后会把0省略,不过也不碍事。
|