首页
社区
课程
招聘
windbg简单分析技巧笔记
发表于: 2020-11-29 00:09 8285

windbg简单分析技巧笔记

2020-11-29 00:09
8285

最近OD用腻了,想要换换口味于是有了这篇文章。这篇文章只是记录了分析过程中windbg的实用技巧,不会细致完整的将病毒跟踪分析。感兴趣的朋友可以自己抽时间跟踪学习。
locky勒索病毒MD5: 5DA38A8EF49834A13219BEDA15A1303C
首先查壳,发现病毒样本并没有加壳。
图片描述
查看资源,发现可疑数据。
图片描述
查看各个区段熵值,只有代码段有较大波动可能代码段存在加密,具体需要调试行为确定。
图片描述
加载病毒到内存,确定模块基址。
图片描述
观察病毒模块详细信息,可以看到病毒具体编译的时间戳未16年。
图片描述
获取病毒文件DOS头相对PE头的偏移值,该值记录在e_lfanew字段中。
图片描述
计算验证是否为NT头的起始位置,可以看到PE字符串标识证明0x400080为PE头。
图片描述
解析NT头结构体信息,因为我是使用的win7 x64位查看的32位进程所以需要使用_IMAGE_NT_HEADERS64解析结构体。
图片描述
根据结构体树,我们可以清晰的看到拓展头中的入口点RVA为0x2af8。
图片描述
windbg虽然不能像OD一样在程序载入的时候自动在入口点设置断点,但是却为我们提供了一个名为$exentry的伪寄存器,它可以帮我们计算入口点的VA地址。直接利用该伪寄存器在入口点下断并按下F5让寄存器跑起来就可以断在OEP。
图片描述
windbg还给我们提供了拓展指令!dh用来详细解析PE文件。例如我们可以使用!dh -s详细查看各个区段内容。
图片描述
进入入口点代码,发现病毒通过计算差值获取全局变量的数据地址。固定差值为0x9864e321。
图片描述
正式分析前我们需要考虑到有的病毒样本中存在睡眠方式反沙箱,我们可以设置条件断点,当触发触发睡眠事件时打印出要睡眠的时间,并且将睡眠时间修改为0。

设置完断点可以让调试器运行起来可以观察到反沙箱睡眠时间。
图片描述
病毒首先调用GetModuleHandleW函数获取kernel32.dll模块在内存中的基址。
图片描述
图片描述
查看返回值与kernel32.dlll中模块基址相同。
图片描述
病毒会在栈中解密以下数据,然后调用call指令跳转到该地址。
图片描述
解密后的地址会申请一块虚拟地址,我这里分配的随机地址为0x3d0000。
图片描述
以每次4字节加减异或解密,并将解密后的数据用pop指令弹入esi寄存器指向的虚拟内存中。
图片描述
设置条件断点,让程序解密完成后自动断住。

图片描述
可以看到中断后的解密数据如下。
图片描述
解密完成后弹出shellcode的首地址到esi,并将LoadLibraryA的API地址作为参数传入压栈,最后调用call指令跳入shellcode区域执行。干扰分析人员的静态分析。
图片描述
使用以下命令将shellcode从内存中转存出来。

图片描述
查看dump出来的shellcode。
图片描述
比较数据一致。
图片描述
可以将shellcode拖入ida观察病毒接下来的恶意逻辑。
图片描述
根据PE数据结构获取各个字段数据在内存中的地址。
图片描述
接下来申请一段虚拟内存,内存大小为0x688字节。
图片描述
将第一段shellcode拷贝到申请的内存空间。 图片描述
调用ret指令弹出新的地址到eip执行第二段恶意逻辑。
图片描述
第一段shellcode整体逻辑如下,用来获取指定API并执行第二段shellcode逻辑。
图片描述
计算基址。
图片描述
申请新的内存空间用于存放被加密的数据。
图片描述
从全局数据段拷贝第三段恶意代码到申请的虚拟内存。
图片描述
拷贝到虚拟内存后循环加减异或解密出PE。
图片描述
修改自身保护属性,目的是为了将当前进程替换成勒索病毒locky。
图片描述
将解密后的PE拷贝到当前进程的模块地址进行PE替换。
图片描述
循环替换PE各个区段。
图片描述
最后将PE装载修复替换后通过jmp esi跳转到替换的PE入口点,我们可以通过$exentry伪寄存器查看入口点VA与ESI一致。
图片描述
图片描述
查看dump下来的勒索程序,发现为VC++6.0编写。
图片描述
ida查看locky勒索软件的WinMain线程,可以看到有两个函数。
图片描述
考虑到病毒本体一般会有检测沙箱和调试器等动作。首先设置一个条件断点检测是否有反调试操作。

设置好条件断点让程序run起来,发现打印以下指令并且调试器中断则证明存在调试器检测代码。
图片描述
利用ida定位到打印指令的上一条指令地址,即调试器检测。
图片描述
我们查看以下peb结构体的BeingDebugged字段。
图片描述
我们可以在windbg入口点设置断点,让程序一运行到入口点就立即清理BeingDebugged字段达到反反调试的目的。

图片描述
如果我们希望在调试的时候不漏掉病毒关键的细节,可以利用windbg的条件断点对病毒行为进行过滤,实现API行为监控的效果。

CreateFileA函数原型。
图片描述
第5个参数属性。
图片描述
设置好条件断点后让程序运行起来,可以发现控制台导出了加密的文件信息。
图片描述
进入勒索病毒入口点后发现有大量的垃圾指令,干扰分析人员分析。
图片描述
图片描述
为了方便演示windbg的条件断点功能,我这里就偷懒不细致分析了。直接把下述断点设置好让windbg自动运行打印出其他关键信息。

文件写入操作,可以看到写入的关键属性。
图片描述
将文件dump出来发现为html勒索信内容,该勒索信详细的介绍了受害者是被何种算法加密以及解密链接的下载以及加密公钥等内容。
图片描述
勒索信内容如下。
图片描述
动态方式获取API。
图片描述
根据windbg动态跟踪API断点,可以发现病毒调用了微软官方加密库使用RSA+AES加密算法对文件进行加密。
图片描述
移动方式覆盖原文件,完成加密。
图片描述
创建注册表子键并设置值为locky。
图片描述
使用命令行显示勒索信息。
图片描述
图片描述
还有一些其他行为感兴趣的可以自己跟踪研究,这里不做赘述。
参考资料:windbg分析locky勒索病毒

bp kernelbase!sleepex ".printf \"Application tried to sleep: %u seconds...\",poi(esp+4)/1000;.echo;ed esp+4 0x0;g"
bp kernelbase!sleepex ".printf \"Application tried to sleep: %u seconds...\",poi(esp+4)/1000;.echo;ed esp+4 0x0;g"
bp 004052b5".if(edx==0x688){};.else{gc;}"
bp 004052b5".if(edx==0x688){};.else{gc;}"
.writemem c:\shellcode.dump 0x3d0000 L0x668        //将数据写到c:\shellcode.dump文件中,从0x3d0000字节开始写,一共写0x688个字节
.writemem c:\shellcode.dump 0x3d0000 L0x668        //将数据写到c:\shellcode.dump文件中,从0x3d0000字节开始写,一共写0x688个字节
bp $exentry "ba r 1 $peb+0x02-0x1000 \"ub\";g"        //当程序执行到入口点对peb所在地址的BeingDebugged字段设置硬件访问断点并打印出读取该字段的汇编代码
64位系统用windbg获取wow64进程的peb会额外产生0x1000的地址偏差,所以要减去0x1000
bp $exentry "ba r 1 $peb+0x02-0x1000 \"ub\";g"        //当程序执行到入口点对peb所在地址的BeingDebugged字段设置硬件访问断点并打印出读取该字段的汇编代码
64位系统用windbg获取wow64进程的peb会额外产生0x1000的地址偏差,所以要减去0x1000
bp $exentry "eb $peb-0x1000+0x02 0x0"
bp $exentry "eb $peb-0x1000+0x02 0x0"
//创建文件行为过滤,一般文件创建依赖于CreateFileA/W API函数。但是该函数不止有创建文件行为,所以需要通过API的第5个参数进行过滤减少打开文件误报
//0n3代表打开文件行为属性,过滤成功后打印第二个参数指向的字符串,不支持%s格式化字符串只能通过%mu进行传递
bp kernelbase!CreateFileW ".if(poi(esp+0x14) != 0n3){.printf \"Creating File: %mu\",poi(esp+0x4);.echo};g"
bp kernelbase!CreateFileA ".if(poi(esp+0x14) != 0n3){.printf \"Creating File: %mu\",poi(esp+0x4);.echo};g"
//创建文件行为过滤,一般文件创建依赖于CreateFileA/W API函数。但是该函数不止有创建文件行为,所以需要通过API的第5个参数进行过滤减少打开文件误报
//0n3代表打开文件行为属性,过滤成功后打印第二个参数指向的字符串,不支持%s格式化字符串只能通过%mu进行传递
bp kernelbase!CreateFileW ".if(poi(esp+0x14) != 0n3){.printf \"Creating File: %mu\",poi(esp+0x4);.echo};g"
bp kernelbase!CreateFileA ".if(poi(esp+0x14) != 0n3){.printf \"Creating File: %mu\",poi(esp+0x4);.echo};g"
bp kernelbase!sleepex ".printf \"Application tried to sleep: %u seconds...\",poi(esp+4)/1000;.echo;ed esp+4 0x0;g"
//创建文件行为过滤,一般文件创建依赖于CreateFileA/W API函数。但是该函数不止有创建文件行为,所以需要通过API的第5个参数进行过滤减少打开文件误报
//0n3代表打开文件行为属性,过滤成功后打印第二个参数指向的字符串,不支持%s格式化字符串只能通过%mu进行传递
bp kernelbase!CreateFileW ".if(poi(esp+0x14) != 0n3){.printf \"Creating File: %mu\",poi(esp+0x4);.echo};g"
bp kernelbase!CreateFileA ".if(poi(esp+0x14) != 0n3){.printf \"Creating File: %ma\",poi(esp+0x4);.echo};g"
//过滤写文件行为
bp kernelbase!WriteFile ".printf \"Dumping file contents from 0x%p as ASCII: %ma\",poi(esp+0x8),poi(esp+0x8);.echo;g"
//过滤删除文件行为
bp kernel32!DeleteFileW ".printf \"Deleting File: %mu\",poi(esp+4);.echo;g"
bp kernel32!DeleteFileA ".printf \"Deleting File: %ma\",poi(esp+4);.echo;g"
//过滤移动文件行为
bp kernel32!MoveFileExW ".printf \"File moved.\";.echo;.printf \"From: %mu\",poi(esp+0x4);.echo;.printf \"To: %mu\",poi(esp+0x8);.echo;g"
bp kernel32!MoveFileExA ".printf \"File moved.\";.echo;.printf \"From: %ma\",poi(esp+0x4);.echo;.printf \"To: %ma\",poi(esp+0x8);.echo;g"
//过滤拷贝文件行为
bp kernel32!CopyFileW ".printf \" Copying file: \";.echo;.printf \"\tFrom: %mu\",poi(esp+0x4);.echo;.printf \"\tTo: %mu\",poi(esp+0x8);.echo;g"
bp kernel32!CopyFileA ".printf \" Copying file: \";.echo;.printf \"\tFrom: %ma\",poi(esp+0x4);.echo;.printf \"\tTo: %ma\",poi(esp+0x8);.echo;g"
//过滤创建注册表子键行为
bp kernel32!RegCreateKeyExA ".printf \"Creating RegKey: %ma\",poi(esp+0x8);.echo;g"
bp kernel32!RegCreateKeyExW ".printf \"Creating RegKey: %mu\",poi(esp+0x8);.echo;g"
//打开注册表键值行为
bp kernel32!RegOpenKeyExA ".printf \"Accessed RegKey: %ma\",poi(esp+0x8);.echo;g"
bp kernel32!RegOpenKeyExW ".printf \"Accessed RegKey: %mu\",poi(esp+0x8);.echo;g"
//过滤键值请求行为
bp kernel32!RegQueryValueExA ".printf \"\tAccessed RegValue: %ma\",poi(esp+0x8);.echo;g"
bp kernel32!RegQueryValueExW ".printf \"\tAccessed RegValue: %mu\",poi(esp+0x8);.echo;g"
//过滤设置注册表键值行为
bp kernel32!RegSetValueExA ".printf \"Setting RegKey %ma to value: %ma\",poi(esp+0x8),poi(esp+0x14);.echo;g"
bp kernel32!RegSetValueExW ".printf \"Setting RegKey %mu to value: %mu\",poi(esp+0x8),poi(esp+0x14);.echo;g"
//过滤创建服务行为
bp advapi32!CreateServiceA ".printf \"Creating Service: \";.echo;.printf \"\tService Name: %ma\",poi(esp+0x4);.echo;.printf \"\tDisplay Name: %ma\",poi(esp+0x8);.echo;g"
bp advapi32!CreateServiceW ".printf \"Creating Service: \";.echo;.printf \"\tService Name: %mu\",poi(esp+0x4);.echo;.printf \"\tDisplay Name: %mu\",poi(esp+0x8);.echo;g"
//过滤创建进程行为
bp kernel32!CreateProcessW ".printf \"Creating Process: %mu\",poi(esp+0x8);.echo;g"
bp kernel32!CreateProcessA ".printf \"Creating Process: %ma\",poi(esp+0x8);.echo;g"
//过滤shell行为以及命令行
bp shell32!shellexecuteW ".printf \"Running Command:\";.echo;.printf \"\tOperation: %mu\",poi(esp+0x8);.echo;.printf \"\tTarget: %mu\",poi(esp+0xC);.echo;.printf \"\tParams: %mu\",poi(esp+0x10);.echo;g"
bp shell32!shellexecuteA ".printf \"Running Command:\";.echo;.printf \"\tOperation: %ma\",poi(esp+0x8);.echo;.printf \"\tTarget: %ma\",poi(esp+0xC);.echo;.printf \"\tParams: %ma\",poi(esp+0x10);.echo;g"
bp shell32!shellExecuteExW ".printf \"Running Command:\";.echo;.printf \"\tOperation: %mu\",poi(poi(esp+0x4)+0xC);.echo;.printf \"\tTarget: %mu\",poi(poi(esp+0x4)+0x10);.echo;.printf \"\tParams: %mu\",poi(poi(esp+0x4)+0x14);.echo;g"
bp shell32!shellExecuteExA ".printf \"Running Command:\";.echo;.printf \"\tOperation: %ma\",poi(poi(esp+0x4)+0xC);.echo;.printf \"\tTarget: %ma\",poi(poi(esp+0x4)+0x10);.echo;.printf \"\tParams: %ma\",poi(poi(esp+0x4)+0x14);.echo;g"
//过滤模块加载行为
bp kernel32!LoadLibraryA ".printf \"Loading LibraryA: %ma\",poi(esp+0x4);.echo;g"
bp kernel32!LoadLibraryW ".printf \"Loading LibraryW: %mu\",poi(esp+0x4);.echo;g"
//过滤动态获取API函数行为
bp kernel32!GetProcAddress ".printf \"\t Looking up function: %ma\",poi(esp+0x8);.echo;g"
bp kernelbase!sleepex ".printf \"Application tried to sleep: %u seconds...\",poi(esp+4)/1000;.echo;ed esp+4 0x0;g"
//创建文件行为过滤,一般文件创建依赖于CreateFileA/W API函数。但是该函数不止有创建文件行为,所以需要通过API的第5个参数进行过滤减少打开文件误报
//0n3代表打开文件行为属性,过滤成功后打印第二个参数指向的字符串,不支持%s格式化字符串只能通过%mu进行传递
bp kernelbase!CreateFileW ".if(poi(esp+0x14) != 0n3){.printf \"Creating File: %mu\",poi(esp+0x4);.echo};g"
bp kernelbase!CreateFileA ".if(poi(esp+0x14) != 0n3){.printf \"Creating File: %ma\",poi(esp+0x4);.echo};g"
//过滤写文件行为
bp kernelbase!WriteFile ".printf \"Dumping file contents from 0x%p as ASCII: %ma\",poi(esp+0x8),poi(esp+0x8);.echo;g"
//过滤删除文件行为
bp kernel32!DeleteFileW ".printf \"Deleting File: %mu\",poi(esp+4);.echo;g"
bp kernel32!DeleteFileA ".printf \"Deleting File: %ma\",poi(esp+4);.echo;g"
//过滤移动文件行为
bp kernel32!MoveFileExW ".printf \"File moved.\";.echo;.printf \"From: %mu\",poi(esp+0x4);.echo;.printf \"To: %mu\",poi(esp+0x8);.echo;g"
bp kernel32!MoveFileExA ".printf \"File moved.\";.echo;.printf \"From: %ma\",poi(esp+0x4);.echo;.printf \"To: %ma\",poi(esp+0x8);.echo;g"

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 6
支持
分享
最新回复 (17)
雪    币: 7079
活跃值: (4015)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
如果windbg有反调试,怎么办?
2020-11-29 09:47
1
雪    币: 4359
活跃值: (4338)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
  Windbg   Dump File... 是敲什么命令?
2020-11-29 10:01
0
雪    币: 3776
活跃值: (5544)
能力值: ( LV7,RANK:115 )
在线值:
发帖
回帖
粉丝
4
dump file用.writemem dump的文件名 dump的地址 L字节数
2020-11-29 10:41
0
雪    币: 3776
活跃值: (5544)
能力值: ( LV7,RANK:115 )
在线值:
发帖
回帖
粉丝
5
反调试只能通过原理修改关键点实现反反调试
2020-11-29 10:41
0
雪    币: 1102
活跃值: (4182)
能力值: ( LV5,RANK:69 )
在线值:
发帖
回帖
粉丝
6
666 收藏一下
有用的时候学习一下
2020-11-29 18:05
0
雪    币: 3
活跃值: (628)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
这叫简单,給大佬跪了。
2020-11-29 22:21
0
雪    币: 919
活跃值: (1340)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
8
bp $exentry "ba r 1 $peb+0x02-0x1000 \"ub\";g"      这条指令用的真6
2021-1-15 18:54
0
雪    币: 2466
活跃值: (4550)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
这么一看感觉windbg的威力平时根本没发挥出来阿...请问楼主有没有啥参考资料,学一下这个
2021-1-15 19:40
0
雪    币: 2674
活跃值: (2304)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
10
感谢分享!很实用!
2021-1-15 22:47
0
雪    币: 3776
活跃值: (5544)
能力值: ( LV7,RANK:115 )
在线值:
发帖
回帖
粉丝
11
Windbg资料主要是看微软MSDN提供的+《Windows高级调试》+《软件调试》+一些大佬发的实战文章
2021-1-16 19:39
0
雪    币: 3776
活跃值: (5544)
能力值: ( LV7,RANK:115 )
在线值:
发帖
回帖
粉丝
12
低调putchar 感谢分享!很实用!
哈哈,谢谢。能帮到您我很开心!
2021-1-16 19:40
0
雪    币: 3776
活跃值: (5544)
能力值: ( LV7,RANK:115 )
在线值:
发帖
回帖
粉丝
13
默NJ 这么一看感觉windbg的威力平时根本没发挥出来阿...请问楼主有没有啥参考资料,学一下这个
Windbg资料主要是看微软MSDN提供的+《Windows高级调试》+《软件调试》+一些大佬发的实战文章。油管也有一些视频可以学习
2021-1-16 19:42
0
雪    币: 3776
活跃值: (5544)
能力值: ( LV7,RANK:115 )
在线值:
发帖
回帖
粉丝
14
丿一叶知秋 bp $exentry "ba r 1 $peb+0x02-0x1000 \"ub\";g"     这条指令用的真6
我也是跟国外大佬的文章做了一遍才学会的
2021-1-16 19:43
0
雪    币: 995
活跃值: (69)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
马克下,摩拜
2021-1-18 15:02
0
雪    币: 5568
活跃值: (2148)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
膜拜大佬的分享,看到很爽
2021-1-28 14:01
0
雪    币: 3776
活跃值: (5544)
能力值: ( LV7,RANK:115 )
在线值:
发帖
回帖
粉丝
17
wanttobeno 膜拜大佬的分享,看到很爽
2021-1-28 15:12
0
雪    币: 2973
活跃值: (4881)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
18
涨姿势了,感谢
2021-4-6 00:01
0
游客
登录 | 注册 方可回帖
返回
//