tElock 0.98 加密壳脱壳
前言:tElock 0.98 脱壳笔记
什么是IAT重定向
跟重定位表的重定位不是一个概念,所以不要混乱了,IAT重定位是壳的一种进阶的保护手段,比起平常的套路(ESP->修复IAT->修复重定位)来讲,这种壳在IAT表上又加了一点手段
正常的来说如下图所示,这是正常的IAT表的结构是如下图所示的
其中比如是GetModuleHandleA函数的调用,如下图所示
那么上面CALL过去的就是如下,跳转过去调用的就是函数的起始地址
当前的程序情况如下,正常的话该地址的是直接CALL 0x460ADC所指向的IAT表中的地址
但是存在IAT重定向,我们会发现不再是DLL中函数的起始地址,而是一个普通的地址
这个普通的地址执行的指令如下,这些就是壳程序自己进行设置的
00A206F7 85E4 TEST ESP,ESP 00A206F9 79 03 JNS SHORT 00A206FE 00A206FB 0F9142 40 SETNO BYTE PTR DS:[EDX+40] 00A206FF B8 D317A200 MOV EAX,0A217D3 00A20704 40 INC EAX 00A20705 FF30 PUSH DWORD PTR DS:[EAX]
脱壳过程
首先进行查壳分析,可以看到特征检测到是一个tElock 0.98的壳程序
因为这个壳开头段内容可能存在硬件和内存断点的检测,在这里用ESP定律已经是不行了,这里定位OEP可以通过GetProcAddress,需要注意的是先让GetProcAddress执行多次次数先,然后再给代码段下执行断点,否则还是会存在断点的检测
寻找OEP参考:https://www.cnblogs.com/zpchcbd/p/12101513.html
通过上面的步骤,可以看到已经来到OEP了
接着老样子先转储当前进程的内存,如下图所示
运行如下图所示,发现程序并不能正常的运行
把程序拖入到CFF Explorer中观察,可以看到导入表0x66BE2都已经无法识别了,如下图所示
0x66BE2跟过去可以看到全部都是空白的,所以这里导入表相关的肯定都是需要修复的
这里用importREC来进行修复,如下图所示
0x460818起始位置,0x460F28结束位置,大小为0x710
会发现存在干扰数据,这里默认的话我们先进行剔除
重新通过importREC进行转存,观察其导入表的情况,如下图所示
然后重新打开,发现还是有问题
上面的两次0xa206f7这个地址出现的问题,将修复好的导入表重新拉入到调试器中,运行会发现有异常,界面直接没有了,从EIP寄存器中可以看到此时就指向了0xa206f7
其实你看上面修复的导入表就会发现问题,这导入表没有相关的kernel32.dll的相关信息,那么程序错误肯定是会有的
那么这里就可以确定importREC进行修复的时候,那两段IID结构是有用的,所以这里重新回到OEP的位置来观察IAT表,可以看到IAT表中间的位置有一大段所谓的干扰数据
0046083C 00A40000 00460840 00A40011 00460844 00A40022 00460848 00A40033 0046084C 00A40041 00460850 00A40050 00460854 00A4005F 00460858 00A4007F
这些干扰数据中可以跟过去看下会发现,全部都是被壳程序进行修改过了,而最终的函数地址的值是先被压入栈中然后RETN指令来进行调用的
00A20677 0BE4 OR ESP,ESP 00A20679 75 01 JNZ SHORT 00A2067C 00A2067B EB 48 JMP SHORT 00A206C5 00A2067D B8 BB17A200 MOV EAX,0A217BB 00A20682 40 INC EAX 00A20683 FF30 PUSH DWORD PTR DS:[EAX] 00A20685 C3 RETN
如果要解决不被修改的话,那么就需要找到壳程序修改这两个IID的情况,需要让壳程序不进行修改才可以,所以这里的话就需要对这些地址进行内存写入断点
接着重新运行程序来进行调试,F9一步一步的跟到正在干扰IAT的时候,如下图所示
0046638A 8B95 62D34000 MOV EDX,DWORD PTR SS:[EBP+40D362] ; edx = 当前程序的基地址(0x400000) 00466390 8B06 MOV EAX,DWORD PTR DS:[ESI] ; 指定IID的偏移地址 00466392 85C0 TEST EAX,EAX ; 判断是否为空 00466394 75 0B JNZ SHORT UnPackMe.004663A1 ; 不空的话就跳转 00466396 8B46 10 MOV EAX,DWORD PTR DS:[ESI+10] 00466399 85C0 TEST EAX,EAX 0046639B ^ 0F84 46FFFFFF JE UnPackMe.004662E7 004663A1 03C2 ADD EAX,EDX ; 程序基址+指定IID的偏移地址,这里就确定了导入表中每个表结构的首地址 004663A3 0385 4ED34000 ADD EAX,DWORD PTR SS:[EBP+40D34E] ; 当前导入表IID中的基址+前面覆盖的次数*4 004663A9 8B18 MOV EBX,DWORD PTR DS:[EAX] ; ebx = 获取对应指向函数名称的偏移地址 004663AB F7C3 00000080 TEST EBX,80000000 004663B1 74 06 JE SHORT UnPackMe.004663B9 004663B3 8120 00000080 AND DWORD PTR DS:[EAX],80000000 004663B9 8B7E 10 MOV EDI,DWORD PTR DS:[ESI+10] ; EDI = 获取下一个导入表的IID结构偏移地址,IID结构之间偏移为0x10 004663BC 03FA ADD EDI,EDX ; EDI = 基址+下一个导入表的IID结构偏移地址 004663BE 80A5 D6CC4000 FF AND BYTE PTR SS:[EBP+40CCD6],0FF 004663C5 0F84 30010000 JE UnPackMe.004664FB 004663CB 80A5 D7CC4000 FF AND BYTE PTR SS:[EBP+40CCD7],0FF 004663D2 0F84 23010000 JE UnPackMe.004664FB 004663D8 89BD 5AD44000 MOV DWORD PTR SS:[EBP+40D45A],EDI ; 保存当前基址+下一个导入表的IID结构偏移地址 004663DE 8B85 52D44000 MOV EAX,DWORD PTR SS:[EBP+40D452] 004663E4 40 INC EAX ; +1 干扰 004663E5 0F84 10010000 JE UnPackMe.004664FB 004663EB 48 DEC EAX ; -1 干扰 004663EC 0F85 B2000000 JNZ UnPackMe.004664A4 ; 开始进行覆盖操作 004663F2 60 PUSHAD 004663F3 8BF7 MOV ESI,EDI 004663F5 2BC0 SUB EAX,EAX 004663F7 40 INC EAX 004663F8 833F 00 CMP DWORD PTR DS:[EDI],0 004663FB 8D7F 04 LEA EDI,DWORD PTR DS:[EDI+4] 004663FE ^ 75 F7 JNZ SHORT UnPackMe.004663F7 00466400 48 DEC EAX 00466401 0F84 EC000000 JE UnPackMe.004664F3 00466407 8BD8 MOV EBX,EAX 00466409 6BC0 31 IMUL EAX,EAX,31 0046640C 6A 04 PUSH 4 0046640E 68 00100000 PUSH 1000 00466413 50 PUSH EAX 00466414 6A 00 PUSH 0 00466416 FF95 ECBA4000 CALL DWORD PTR SS:[EBP+40BAEC] 0046641C 85C0 TEST EAX,EAX 0046641E 0F84 CF000000 JE UnPackMe.004664F3 00466424 8BFE MOV EDI,ESI 00466426 8BCB MOV ECX,EBX 00466428 8BF8 MOV EDI,EAX 0046642A 8985 56D44000 MOV DWORD PTR SS:[EBP+40D456],EAX 00466430 8BCB MOV ECX,EBX 00466432 6BDB 29 IMUL EBX,EBX,29 00466435 03DF ADD EBX,EDI 00466437 891C24 MOV DWORD PTR SS:[ESP],EBX 0046643A B0 B8 MOV AL,0B8 0046643C 6A 00 PUSH 0 0046643E 50 PUSH EAX 0046643F 53 PUSH EBX 00466440 0FB74424 08 MOVZX EAX,WORD PTR SS:[ESP+8] 00466445 50 PUSH EAX 00466446 8D85 14BB4000 LEA EAX,DWORD PTR SS:[EBP+40BB14] 0046644C 0FB618 MOVZX EBX,BYTE PTR DS:[EAX] 0046644F FF0C24 DEC DWORD PTR SS:[ESP] 00466452 7E 09 JLE SHORT UnPackMe.0046645D 00466454 40 INC EAX 00466455 03C3 ADD EAX,EBX 00466457 ^ EB F3 JMP SHORT UnPackMe.0046644C 00466459 0300 ADD EAX,DWORD PTR DS:[EAX] 0046645B 0000 ADD BYTE PTR DS:[EAX],AL 0046645D 40 INC EAX 0046645E 8A38 MOV BH,BYTE PTR DS:[EAX] 00466460 883F MOV BYTE PTR DS:[EDI],BH 00466462 47 INC EDI 00466463 FECB DEC BL 00466465 ^ 7F F6 JG SHORT UnPackMe.0046645D 00466467 5B POP EBX 00466468 5B POP EBX 00466469 58 POP EAX 0046646A AA STOS BYTE PTR ES:[EDI] 0046646B FF0424 INC DWORD PTR SS:[ESP] 0046646E 832424 0F AND DWORD PTR SS:[ESP],0F 00466472 4B DEC EBX 00466473 891F MOV DWORD PTR DS:[EDI],EBX 00466475 43 INC EBX 00466476 83C3 04 ADD EBX,4 00466479 83C7 04 ADD EDI,4 0046647C B8 40FF30C3 MOV EAX,C330FF40 00466481 AB STOS DWORD PTR ES:[EDI] 00466482 B0 B8 MOV AL,0B8 00466484 49 DEC ECX 00466485 ^ 7F B7 JG SHORT UnPackMe.0046643E 00466487 AA STOS BYTE PTR ES:[EDI] 00466488 E8 00000000 CALL UnPackMe.0046648D 0046648D 58 POP EAX 0046648E AB STOS DWORD PTR ES:[EDI] 0046648F B8 90FF30C3 MOV EAX,C330FF90 00466494 AB STOS DWORD PTR ES:[EDI] 00466495 58 POP EAX 00466496 61 POPAD 00466497 83A5 FBCA4000 00 AND DWORD PTR SS:[EBP+40CAFB],0 0046649E 89BD 52D44000 MOV DWORD PTR SS:[EBP+40D452],EDI 004664A4 8D85 14BB4000 LEA EAX,DWORD PTR SS:[EBP+40BB14] ; eax = 要填充IAT的地址 004664AA FFB5 FBCA4000 PUSH DWORD PTR SS:[EBP+40CAFB] ; 函数长度 004664B0 0FB608 MOVZX ECX,BYTE PTR DS:[EAX] ; 函数长度 004664B3 FF0C24 DEC DWORD PTR SS:[ESP] 004664B6 7E 05 JLE SHORT UnPackMe.004664BD 004664B8 40 INC EAX 004664B9 03C1 ADD EAX,ECX 004664BB ^ EB F3 JMP SHORT UnPackMe.004664B0 ; 循环某些东西 004664BD 890C24 MOV DWORD PTR SS:[ESP],ECX 004664C0 FF85 FBCA4000 INC DWORD PTR SS:[EBP+40CAFB] 004664C6 83A5 FBCA4000 0F AND DWORD PTR SS:[EBP+40CAFB],0F 004664CD 8BBD 52D44000 MOV EDI,DWORD PTR SS:[EBP+40D452] 004664D3 8B85 5AD44000 MOV EAX,DWORD PTR SS:[EBP+40D45A] ; EAX = IAT表的首地址 004664D9 0385 4ED34000 ADD EAX,DWORD PTR SS:[EBP+40D34E] ; EAX = IAT表的首地址+要覆盖的偏移值 004664DF 8B8D 56D44000 MOV ECX,DWORD PTR SS:[EBP+40D456] ; ECX = 要覆盖原IAT中的值 004664E7 58 POP EAX ; EAX = 要覆盖的偏移值 004664E8 83C0 09 ADD EAX,9 ; ADD = 计算下一次要覆盖的偏移值+9 004664EB 0185 56D44000 ADD DWORD PTR SS:[EBP+40D456],EAX ; 保存下一次要覆盖的偏移值 004664F1 EB 08 JMP SHORT UnPackMe.004664FB ; 跳转 004664F3 838D 52D44000 FF OR DWORD PTR SS:[EBP+40D452],FFFFFFF> 004664FA 61 POPAD 004664FB 03BD 4ED34000 ADD EDI,DWORD PTR SS:[EBP+40D34E] ; 加上偏移 00466501 85DB TEST EBX,EBX 00466503 0F84 C7000000 JE UnPackMe.004665D0 00466509 F7C3 00000080 TEST EBX,80000000 0046650F 6A 00 PUSH 0 00466511 75 0F JNZ SHORT UnPackMe.00466522 00466513 8D5C13 02 LEA EBX,DWORD PTR DS:[EBX+EDX+2] ; 获得下一个IAT函数的名称 00466517 803B 00 CMP BYTE PTR DS:[EBX],0 ; 判断是否是有值的 0046651A 0F84 93000000 JE UnPackMe.004665B3 ; 有的话就不跳转 00466520 EB 45 JMP SHORT UnPackMe.00466567 ; JMP 00466522 FF0424 INC DWORD PTR SS:[ESP] 00466525 66:85DB TEST BX,BX 00466528 0F84 85000000 JE UnPackMe.004665B3 0046652E 8B85 4AD34000 MOV EAX,DWORD PTR SS:[EBP+40D34A] 00466534 3B85 42D44000 CMP EAX,DWORD PTR SS:[EBP+40D442] 0046653A 75 2B JNZ SHORT UnPackMe.00466567 0046653C 81E3 FFFFFF7F AND EBX,7FFFFFFF 00466542 8BD3 MOV EDX,EBX 00466544 8D1495 FCFFFFFF LEA EDX,DWORD PTR DS:[EDX*4-4] 0046654B 8B9D 4AD34000 MOV EBX,DWORD PTR SS:[EBP+40D34A] 00466551 8B43 3C MOV EAX,DWORD PTR DS:[EBX+3C] 00466554 8B4418 78 MOV EAX,DWORD PTR DS:[EAX+EBX+78] 00466558 035C18 1C ADD EBX,DWORD PTR DS:[EAX+EBX+1C] 0046655C 8B041A MOV EAX,DWORD PTR DS:[EDX+EBX] 0046655F 0385 4AD34000 ADD EAX,DWORD PTR SS:[EBP+40D34A] 00466565 EB 13 JMP SHORT UnPackMe.0046657A 00466567 81E3 FFFFFF7F AND EBX,7FFFFFFF 0046656D 53 PUSH EBX ; 压入下一个IAT要获取的函数名称 0046656E FFB5 4AD34000 PUSH DWORD PTR SS:[EBP+40D34A] ; 当前函数名称对应的DLL模块 00466574 FF95 E0BA4000 CALL DWORD PTR SS:[EBP+40BAE0] ; 通过GetProcAddress来获取函数地址 0046657A 40 INC EAX ; 函数地址+1 0046657B 48 DEC EAX ; 函数地址-1 0046657C 75 33 JNZ SHORT UnPackMe.004665B1 0046657E 58 POP EAX 0046657F F9 STC 00466580 ^ 0F82 61FDFFFF JB UnPackMe.004662E7 00466586 47 INC EDI 00466587 44 INC ESP 00466588 49 DEC ECX 00466589 3332 XOR ESI,DWORD PTR DS:[EDX] 0046658B 2E:44 INC ESP ; 多余的前缀 0046658D 4C DEC ESP 0046658E 4C DEC ESP 0046658F 55 PUSH EBP 00466590 53 PUSH EBX 00466591 45 INC EBP 00466592 52 PUSH EDX 00466593 3332 XOR ESI,DWORD PTR DS:[EDX] 00466595 2E:44 INC ESP ; 多余的前缀 00466597 4C DEC ESP 00466598 4C DEC ESP 00466599 53 PUSH EBX 0046659A 48 DEC EAX 0046659B 45 INC EBP 0046659C 4C DEC ESP 0046659D 4C DEC ESP 0046659E 3332 XOR ESI,DWORD PTR DS:[EDX] 004665A0 2E:44 INC ESP ; 多余的前缀 004665A2 4C DEC ESP 004665A3 4C DEC ESP 004665A4 4B DEC EBX 004665A5 45 INC EBP 004665A6 52 PUSH EDX 004665A7 4E DEC ESI 004665A8 45 INC EBP 004665A9 4C DEC ESP 004665AA 3332 XOR ESI,DWORD PTR DS:[EDX] 004665AC 2E:44 INC ESP ; 多余的前缀 004665AE 4C DEC ESP 004665AF 4C DEC ESP 004665B0 0B89 07584874 OR ECX,DWORD PTR DS:[ECX+74485807] 004665B6 0D 40F86689 OR EAX,8966F840 004665BB 43 INC EBX 004665BC FE88 03433803 DEC BYTE PTR DS:[EAX+3384303] 004665C2 ^ 75 F9 JNZ SHORT UnPackMe.004665BD ; 遍历根据函数的长度来进行清空对应的次数 004665C4 8385 4ED34000 04 ADD DWORD PTR SS:[EBP+40D34E],4 004665CB ^ E9 BAFDFFFF JMP UnPackMe.0046638A ; 跳转回去准备覆盖下一个IAT地址
小知识点:如何确定这就是我们原来的IAT表呢?我自己用的就是你可以用当前的基址加上当前要被覆盖的IAT表的的值,这两个相加的值就是原来对应当前函数地址的函数名称,如下图所示
4627A0 = 40000 + 0x627A0,这里的话就可以看到正在覆盖SetTextAlign的原IAT表
那么找到关键的跳转这样就可以让壳程序不会对进行修改原来的IAT表了,这里的话就需要先看下,我虽然前面有注释写了,但是具体的算法流程我不是非常的详细,所以我这里还是看下没有覆盖的情况下流程是怎么走的,比如00460818,0046081C,00460820这些都是没有被覆盖的
00460818 77DA6C27 ADVAPI32.RegCloseKey 0046081C 77DA7852 ADVAPI32.RegOpenKeyExA 00460820 77DAE9F4 ADVAPI32.RegCreateKeyExA 00460824 77DAEAE7 ADVAPI32.RegSetValueExA 00460828 77DA7ABB ADVAPI32.RegQueryValueExA 0046082C 00000000 00460830 5D1765CF COMCTL32.InitCommonControls 00460834 5D1803D8 COMCTL32.ImageList_Destroy 00460838 00000000 0046083C 00A40000
那我就给00460818下内存写入断点进行观察填充IAT表的流程,重新运行,多次F9来到如下图所示
这里可以发现被覆盖的IAT表的顺序是在正常填充IAT表之前的,因为此时正常IAT的地址都还没填充,但是此时下面的IAT表已经被覆盖了
此时继续F9运行断下,这个时候已经可以看到正常的IAT表填充流程了,而且同样是在这个大循环跳转里面
同样的会来到这个起始地点 0x46638A
0046638A 8B95 62D34000 MOV EDX,DWORD PTR SS:[EBP+40D362] ; edx = 当前程序的基地址(0x400000)
这里需要注意到这里,这个跳转在正常的填充IAT的时候进行跳转
004663D2 /0F84 23010000 JE UnPackMe.004664FB
跳转之后,可以看到下面的图中直接就绕过了覆盖IAT的地方
然后往下走又是继续开始填充
到这里的话就可以知道0x004663D2的这个跳转是一个关键的跳转了,所以这里重新加载,把IAT的整个区域都进行内存写入断点,F9,等最后写入到00460F28的停止
需要注意:最后写入到00460F28的停止这些过程都还不是开始填充的过程,这时候还在解密代码段的过程
到了之后,再给0x004663D2 JMP指令
然后重新看下IAT表此时全部都已经恢复
修复IAT如下图所示,IAT表信息如下
4271B0 OEP
00460818 开始
00460F28 结束
0x710 IAT大小
可以看到此时的kernel32.dll都已经是好的了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
2020-03-10 内网渗透(四)