本帖最后由 mmortalyi 于 2023-12-23 22:45 编辑
朋友发来的一个视频加密软件,跟了许久,留下点东西分享出来。 0、由于视频文件比较大,视频软件不会一次读取所有文件处理,而是分段处理,这样就可以跟踪到处理函数,而这个处理函数都是动态释放的,好在是函数没有加密。 1、一般处理函数的参数也就是,函数(密码相关信息,buffer,size)这样的,处理完以后下断通过od的脚本导出即可。 dma [esp-8], 8000, "z:\03.txt" jmp justgo cmps: cmp eip,100790ba //解密完的地方 je getapi jmp end getapi: dma [esp-8], 8000, "z:\03.txt" //在堆栈里翻出buffer地址[esp-8],大小0x8000,追加存入文件 "z:\03.txt" justgo: RUN jmp cmps end:
这样虽然可以,但是一次只能处理32K的数据,od断点处理的效率很低,而且还有同步在播放,卡顿。于是乎想了一种办法来处理,何不直接用汇编打开文件来处理呢,不要让他播放了,思路是读文件,给到buffer,解密,再读文件。 通过脚本保存文件。最后还是觉得太麻烦,既然都写汇编了,何不直接把写文件也弄上呢? 3、做做准备工作 了解下api读写函数的参数调用,堆栈等,参数尽量通过堆栈存储和访问,尽量优雅的还原现场。相关的call可以直接修改为api函数或者解密函数,避免每次od跟踪数据地址不同的情况。用二进制数据保存写好的指令,中间预留一些9090,防止修改字节数不够。 准备了如下一些api: 打开文件: 0019EF90 7ADDA8F4 /CALL 到 CreateFileA 来自 MSVBVM60.7ADDA8EE 0019EF94 0019F008 |FileName = "d:\nonamea.txt" 0019EF98 C0000000 |Access = GENERIC_READ|GENERIC_WRITE 0019EF9C 00000003 |ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE 0019EFA0 0019EFC8 |pSecurity = 0019EFC8 0019EFA4 00000004 |Mode = OPEN_ALWAYS 0019EFA8 00000080 |Attributes = NORMAL 0019EFAC 00000000 \hTemplateFile = NULL
读文件 0019F09C 55600873 /CALL 到 ReadFile 来自 MSVBVM60.5560086D 0019F0A0 000002D0 |hFile = 000002D0 (window) 0019F0A4 005933D0 |Buffer = 005933D0 0019F0A8 000018F0 |BytesToRead = 18F0 (6384.) 0019F0AC 0019F0C8 |pBytesRead = 0019F0C8 0019F0B0 00000000 \pOverlapped = NULL
写文件 0019EC58 7ADDB2AF /CALL 到 WriteFile 来自 MSVBVM60.7ADDB2A9 0019EC5C 000002E4 |hFile = 000002E4 0019EC60 007846F4 |Buffer = 007846F4 0019EC64 00000004 |nBytesToWrite = 4 0019EC68 0019EC8C |pBytesWritten = 0019EC8C 0019EC6C 00000000 \pOverlapped = NULL
移动文件读取位置 76DBBF34 6A 00 push 0x0 76DBBF36 6A 00 push 0x0 76DBBF38 6A 03 push 0x3 76DBBF3A 68 48020000 push 0x248 76DBBF3F E8 5C2FFAFF call KERNEL32.SetFilePointer ; jmp 到 KERNELBA.SetFilePointer
关闭文件 7ADDB381 50 push eax 7ADDB382 FF15 5412DD7A call dword ptr ds:[<&KERNEL32.CloseHandl>; KERNEL32.CloseHandle
4、直接上代码,后面都写好了注释了。 10007D4D 90 nop 10007D4E 90 nop 10007D4F 90 nop 10007D50 83EC 20 sub esp,0x20 ; 新eip开始,此处临时开辟32字节,输入文件名,打开文件,压入句柄,此处执行2次 10007D53 6A 00 push 0x0 ; 给打开文件输入参数 10007D55 68 80000000 push 0x80 10007D5A 6A 04 push 0x4 10007D5C 6A 00 push 0x0 10007D5E 6A 03 push 0x3 10007D60 68 000000C0 push 0xC0000000 10007D65 8BC4 mov eax,esp 10007D67 83C0 18 add eax,0x18 10007D6A 50 push eax 10007D6B E8 604CC466 call KERNELBA.CreateFileA ; 建立文件,eax返回句柄 10007D70 83C4 20 add esp,0x20 ; 回收堆栈 10007D73 50 push eax ; 此处保存需要处理的文件句柄,并从上次新建eip处,执行第二次,新建新文件,压入句柄 10007D74 6A 01 push 0x1 ; 占位,后来用作写入文件反馈的写入字节 10007D76 6A 02 push 0x2 ; 占位,后来用作读入文件反馈的读入字节 10007D78 6A 03 push 0x3 ; 修改为需要处理的buffer 10007D7A 6A 04 push 0x4 ; 读取文件的句柄 10007D7C 6A 05 push 0x5 ; 修改为读取反馈的指针,及push2 处的堆栈地址 10007D7E 6A 06 push 0x6 ; 修改为需要处理的文件开始指针 10007D80 6A 00 push 0x0 ; 文件指针移动的必要参数 10007D82 6A 00 push 0x0 10007D84 FF7424 08 push dword ptr ss:[esp+0x8] ; 压入文件偏移 10007D88 FF7424 14 push dword ptr ss:[esp+0x14] ; 压入文件句柄 10007D8C 90 nop 10007D8D 90 nop 10007D8E 90 nop 10007D8F E8 0C714E65 call KERNEL32.SetFilePointer ; jmp 到 KERNELBA.SetFilePointer 10007D94 6A 00 push 0x0 ; 读取文件的参数 10007D96 FF7424 08 push dword ptr ss:[esp+0x8] 10007D9A 90 nop 10007D9B 68 00800000 push 0x8000 ; 每次读取字节数 10007DA0 FF7424 18 push dword ptr ss:[esp+0x18] ; buffer 10007DA4 90 nop 10007DA5 FF7424 18 push dword ptr ss:[esp+0x18] ; 读文件的句柄 10007DA9 90 nop 10007DAA E8 61704E65 call KERNEL32.ReadFile ; jmp 到 KERNELBA.ReadFile 10007DAF 90 nop 10007DB0 83F8 01 cmp eax,0x1 10007DB3 75 73 jnz Xcorem.10007E28 ; 读取成功为1,否者跳走,下断点 10007DB5 3E:8B4424 10 mov eax,dword ptr ds:[esp+0x10] 10007DBA 3D 00800000 cmp eax,0x8000 ; 比较读取的字节数,如果不是0x8000证明文件读取完毕 10007DBF 75 6F jnz Xcorem.10007E30 ; 跳转到最后半包数据的处理 10007DC1 B8 6057AA07 mov eax,0x7AA5760 ; 初始化解密参数信息 10007DC6 C700 656C8174 mov dword ptr ds:[eax],0x74816C65 10007DCC C740 04 EC0DB>mov dword ptr ds:[eax+0x4],0xE5B80DEC 10007DD3 C740 08 5EE5F>mov dword ptr ds:[eax+0x8],0xFAFAE55E 10007DDA C740 0C 11F82>mov dword ptr ds:[eax+0xC],0xDC26F811 ; 初始化解密参数信息 10007DE1 68 00800000 push 0x8000 ; 解密字节数 10007DE6 FF7424 10 push dword ptr ss:[esp+0x10] ; 解密的buffer 10007DEA FF7424 2C push dword ptr ss:[esp+0x2C] ; 解密的依赖信息,包含上面初始化的信息 10007DEE E8 1D95FFFF call corem.#11 ; 调用解密函数,解密后,还是存在buffer里面 10007DF3 90 nop 10007DF4 6A 00 push 0x0 ; 写参数 pOverlapped = NULL 10007DF6 8BC4 mov eax,esp 10007DF8 83C0 18 add eax,0x18 10007DFB 50 push eax ; pBytesWritten 10007DFC FF7424 18 push dword ptr ss:[esp+0x18] ; 写字节 0x8000 10007E00 FF7424 18 push dword ptr ss:[esp+0x18] ; buffer 10007E04 FF7424 28 push dword ptr ss:[esp+0x28] ; hFile 写文件的句柄。 10007E08 E8 F3704E65 call KERNEL32.WriteFile ; jmp 到 KERNELBA.WriteFile 10007E0D 33C0 xor eax,eax ;清空eax 10007E0F 034424 10 add eax,dword ptr ss:[esp+0x10] ;文件偏移 10007E13 030424 add eax,dword ptr ss:[esp] ;文件偏移加上本次处理的字节。 10007E16 890424 mov dword ptr ss:[esp],eax ;修改下次文件偏移的地址 10007E19 ^ E9 62FFFFFF jmp corem.10007D80 ;回跳循环执行代码了。 10007E1E 90 nop 10007E1F 90 nop 10007E20 90 nop 10007E21 90 nop 10007E22 90 nop 10007E23 90 nop 10007E24 90 nop 10007E25 90 nop 10007E26 90 nop 10007E27 90 nop 10007E28 90 nop ; 读取失败的断点 10007E29 90 nop 10007E2A 90 nop 10007E2B 90 nop 10007E2C 90 nop 10007E2D 90 nop 10007E2E 90 nop 10007E2F 90 nop 10007E30 B8 6057AA07 mov eax,0x7AA5760 ; 处理最后半包数据,初始化解密参数 10007E35 C700 656C8174 mov dword ptr ds:[eax],0x74816C65 10007E3B C740 04 EC0DB>mov dword ptr ds:[eax+0x4],0xE5B80DEC 10007E42 C740 08 5EE5F>mov dword ptr ds:[eax+0x8],0xFAFAE55E 10007E49 C740 0C 11F82>mov dword ptr ds:[eax+0xC],0xDC26F811 10007E50 FF7424 10 push dword ptr ss:[esp+0x10] 10007E54 90 nop 10007E55 FF7424 10 push dword ptr ss:[esp+0x10] 10007E59 FF7424 2C push dword ptr ss:[esp+0x2C] 10007E5D E8 AE94FFFF call corem.#11 ; 解密函数 10007E62 90 nop 10007E63 6A 00 push 0x0 10007E65 8BC4 mov eax,esp 10007E67 83C0 18 add eax,0x18 10007E6A 50 push eax 10007E6B FF7424 18 push dword ptr ss:[esp+0x18] 10007E6F FF7424 18 push dword ptr ss:[esp+0x18] 10007E73 FF7424 28 push dword ptr ss:[esp+0x28] 10007E77 E8 84704E65 call KERNEL32.WriteFile ; jmp 到 KERNELBA.WriteFile 10007E7C FF7424 18 push dword ptr ss:[esp+0x18] ; 压入句柄,下一步关闭解密好的文件 10007E80 E8 8B694E65 call KERNEL32.CloseHandle ; jmp 到 KERNELBA.CloseHandle 10007E85 83C4 20 add esp,0x20 ; 平衡堆栈,跳回去执行本来的解密函数 10007E88 ^ E9 8394FFFF jmp corem.#11 10007E8D 90 nop 10007E8E 90 nop
5、最后的成品二进制代码 90 90 90 83 EC 20 6A 00 68 80 00 00 00 6A 04 6A 00 6A 03 68 00 00 00 C0 8B C4 83 C0 18 50 E8 60 4C CC 6D 83 C4 20 50 6A 01 6A 02 6A 03 6A 04 6A 05 6A 06 6A 00 6A 00 FF 74 24 08 FF 74 24 14 90 90 90 E8 4C C8 CE 6D 6A 00 FF 74 24 08 90 68 00 80 00 00 FF 74 24 18 90 FF 74 24 18 90 E8 61 CB CE 6D 90 83 F8 01 75 73 3E 8B 44 24 10 3D 00 80 00 00 75 6F B8 60 57 AA 07 C7 00 65 6C 81 74 C7 40 04 EC 0D B8 E5 C7 40 08 5E E5 FA FA C7 40 0C 11 F8 26 DC 68 00 80 00 00 FF 74 24 10 FF 74 24 2C E8 5F 17 71 08 90 6A 00 8B C4 83 C0 18 50 FF 74 24 18 FF 74 24 18 FF 74 24 28 E8 03 D6 CE 6D 33 C0 03 44 24 10 03 04 24 89 04 24 E9 62 FF FF FF 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 B8 60 57 AA 07 C7 00 65 6C 81 74 C7 40 04 EC 0D B8 E5 C7 40 08 5E E5 FA FA C7 40 0C 11 F8 26 DC FF 74 24 10 90 FF 74 24 10 FF 74 24 2C E8 F0 16 71 08 90 6A 00 8B C4 83 C0 18 50 FF 74 24 18 FF 74 24 18 FF 74 24 28 E8 94 D5 CE 6D FF 74 24 18 E8 4B 65 CC 6D 83 C4 20 E9 83 94 FF FF 90 90 90 90 90 90 90
注:若转载请注明大神论坛来源(本贴地址)与作者信息。
|