本帖最后由 feiyao32 于 2023-12-20 00:24 编辑
朋友发来的一个视频加密软件,跟了许久,留下点东西分享出来。
0、由于视频文件比较大,视频软件不会一次读取所有文件处理,而是分段处理,这样就可以跟踪到处理函数,而这个处理函数都是动态释放的,好在是函数没有加密。
1、一般处理函数的参数也就是,函数(密码相关信息,buffer,size)这样的,处理完以后下断通过od的脚本导出即可。
[C++] 纯文本查看 复制代码 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:
打开文件:
[Asm] 纯文本查看 复制代码 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
读文件
[Asm] 纯文本查看 复制代码 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
写文件
[Asm] 纯文本查看 复制代码 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
移动文件读取位置
[Asm] 纯文本查看 复制代码 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
关闭文件
[Asm] 纯文本查看 复制代码 7ADDB381 50 push eax
7ADDB382 FF15 5412DD7A call dword ptr ds:[<&KERNEL32.CloseHandl>; KERNEL32.CloseHandle
4、直接上代码,后面都写好了注释了。
[Asm] 纯文本查看 复制代码 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、最后的成品二进制代码
[Asm] 纯文本查看 复制代码 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
|