- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
在B站,公众号上发了一篇 AOT 的文章后,没想到反响还是挺大的,都称赞这个东西能抗反编译,可以让破解难度极大提高,可能有很多朋友对逆向不了解,以为用 ILSpy , Reflector , DnSpy 这些工具打不开就觉得很安全,其实不然,在 OllyDbg , IDA , WinDBG 这些逆向工具面前一样是裸奔.
既然大家都很感兴趣,那这篇就和大家聊一聊.
修改程序数据在逆向中再正常不过了,由于目前的 AOT 只能发布成 x64 ,这里就用 WinDbg 做下演示,首先看下例子.
internal class Program
{
static void Main(string[] args)
{
while (true)
{
Console.WriteLine("hello world!");
Thread.Sleep(1000);
}
}
}
程序在不断的输出,接下来我们将 hello world 中的 world 给去掉,操作手法非常简单,先内存搜索找到 hello world ,然后修改 length=5 即可.
0:005> lm
start end module name
00007ff7`95b70000 00007ff7`95e5d000 ConsoleApp1 C (private pdb symbols)
0:005> s-u 00007ff7`95b70000 L?0x00007ff7`95e5d000 hello
00007ff7`95e1c41c 0068 0065 006c 006c 006f 0020 0077 006f h.e.l.l.o. .w.o.
0:000> dp 00007ff795e1c41c-0x4 L1
00007ff7`95e1c418 00650068`0000000c
0:000> eq 00007ff7`95e1c418 00650068`00000005
0:000> g
AOT 再怎么牛,它终还是个托管程序,既然是托管程序自然就有托管堆,托管堆中就有所有的托管数据,玩过 SOS.dll 朋友应该知道,用 !eeheap -gc 就能把托管堆给显示出来,比如下面这样.
0:022> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x000002414D891030
generation 1 starts at 0x000002414D891018
generation 2 starts at 0x000002414D891000
ephemeral segment allocation context: none
segment begin allocated committed allocated size committed size
000002414D890000 000002414D891000 000002414D8D1FE8 000002414D8D2000 0x40fe8(266216) 0x41000(266240)
Large object heap starts at 0x000002415D891000
segment begin allocated committed allocated size committed size
000002415D890000 000002415D891000 000002415D891018 000002415D892000 0x18(24) 0x1000(4096)
Pinned object heap starts at 0x0000024165891000
0000024165890000 0000024165891000 0000024165899C10 00000241658A2000 0x8c10(35856) 0x11000(69632)
Total Allocated Size: Size: 0x49c10 (302096) bytes.
Total Committed Size: Size: 0x42000 (270336) bytes.
------------------------------
GC Allocated Heap Size: Size: 0x49c10 (302096) bytes.
GC Committed Heap Size: Size: 0x42000 (270336) bytes.
虽然目前的 AOT 不支持 SOS 扩展,无法显示出托管堆,但一点关系都没有,SOS 是通过 DataAccess 去挖的,大不来我手工挖一下就好了哈,接下来就是怎么挖的问题了,熟悉 CLR 的朋友应该知道所谓的托管堆在内部用的是 generation_table[] 一维数据来维护的,以 代 的方式来划分,代的落地是用 heap_segment 来表示的, 参考代码如下:
generation gc_heap::generation_table [total_generation_count];
enum gc_generation_num
{
// small object heap includes generations [0-2], which are "generations" in the general sense.
soh_gen0 = 0,
soh_gen1 = 1,
soh_gen2 = 2,
max_generation = soh_gen2,
// large object heap, technically not a generation, but it is convenient to represent it as such
loh_generation = 3,
// pinned heap, a separate generation for the same reasons as loh
poh_generation = 4,
uoh_start_generation = loh_generation,
// number of ephemeral generations
ephemeral_generation_count = max_generation,
// number of all generations
total_generation_count = poh_generation + 1
};
接下来用 x 命令看下数组内容,代码如下:
0:000> x ConsoleApp1!WKS::gc_heap::generation_table
00007ff7`95e25010 ConsoleApp1!WKS::gc_heap::generation_table = class WKS::generation [5]
0:000> dx -r1 (*((ConsoleApp1!WKS::generation (*)[5])0x7ff795e25010))
(*((ConsoleApp1!WKS::generation (*)[5])0x7ff795e25010)) [Type: WKS::generation [5]]
[0] [Type: WKS::generation]
...
[4] [Type: WKS::generation]
0:000> dx -r1 (*((ConsoleApp1!WKS::generation *)0x7ff795e25010))
(*((ConsoleApp1!WKS::generation *)0x7ff795e25010)) [Type: WKS::generation]
[+0x038] start_segment : 0x25100000000 [Type: WKS::heap_segment *]
[+0x040] allocation_start : 0x25100001030 : 0x38 [Type: unsigned char *]
[+0x048] allocation_segment : 0x25100000000 [Type: WKS::heap_segment *]
[+0x0d0] allocation_size : 0x0 [Type: unsigned __int64]
[+0x100] gen_num : 0 [Type: int]
...
0:000> dx -r1 ((ConsoleApp1!WKS::heap_segment *)0x25100000000)
((ConsoleApp1!WKS::heap_segment *)0x25100000000) : 0x25100000000 [Type: WKS::heap_segment *]
[+0x000] allocated : 0x25100001048 : 0x90 [Type: unsigned char *]
[+0x008] committed : 0x25100012000 : Unable to read memory at Address 0x25100012000 [Type: unsigned char *]
[+0x010] reserved : 0x25110000000 : 0x18 [Type: unsigned char *]
[+0x018] used : 0x25100009fe0 : 0x0 [Type: unsigned char *]
[+0x020] mem : 0x25100001000 : 0x38 [Type: unsigned char *]
[+0x028] flags : 0x0 [Type: unsigned __int64]
[+0x030] next : 0x0 [Type: WKS::heap_segment *]
...
上面的这些字段就描述出了 !eeheap -gc 的结果,接下来想挖什么,提取什么我就不过多介绍了.
提取 托管线程 列表也是非常重要的, 它能指示出很多信息,一般用 !t 命令就能显示,输出如下:
0:022> !t
ThreadCount: 17
UnstartedThread: 0
BackgroundThread: 6
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
Lock
DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
0 1 4128 000002414BDB8C70 2a020 Preemptive 000002414D8C6108:000002414D8C8000 000002414bdaf8f0 -00001 MTA
6 2 4458 000002414BDE5EB0 2b220 Preemptive 0000000000000000:0000000000000000 000002414bdaf8f0 -00001 MTA (Finalizer)
7 4 23e8 000002416DDB15C0 102b220 Preemptive 000002414D8C9250:000002414D8CA000 000002414bdaf8f0 -00001 MTA (Threadpool Worker)
...
20 17 50a8 000002416DE43DD0 102b220 Preemptive 000002414D8BC2D0:000002414D8BDFD0 000002414bdaf8f0 -00001 MTA (Threadpool Worker)
21 18 57d4 000002416DE628E0 8029220 Preemptive 000002414D8CC2A8:000002414D8CE000 000002414bdaf8f0 -00001 MTA (Threadpool Completion Port)
既然目前的 SOS 不支持,同样可以手工到 CLR 中去挖,熟悉的朋友应该知道 !t 的数据源来自于 ThreadStore::s_pThreadStore 下的 m_ThreadList 集合,它以链表的形式串联了每个线程的 LinkPtr 字段,但可惜的是,在 AOT 中,这一块已经重写了,由 g_pTheRuntimeInstance 全局变量下的 m_ThreadList 来维护了.
为了方便观察,多生成几个 Thread.
static void Main(string[] args)
{
Debugger.Break();
var tasks = Enumerable.Range(0, 10).Select(m => new Thread(() =>
{
Console.WriteLine($"tid={Thread.CurrentThread.ManagedThreadId} 已执行!");
Console.ReadLine();
}));
foreach (var item in tasks)
{
item.Start();
}
Console.ReadLine();
}
程序跑起来后,深挖 g_pTheRuntimeInstance 全局变量即可.
0:015> x ConsoleApp1!g_pTheRuntimeInstance
00007ff7`0155ee20 ConsoleApp1!g_pTheRuntimeInstance = 0x00000291`cb5b9300
0:015> dx -r1 ((ConsoleApp1!RuntimeInstance *)0x291cb5b9300)
((ConsoleApp1!RuntimeInstance *)0x291cb5b9300) : 0x291cb5b9300 [Type: RuntimeInstance *]
[+0x000] m_pThreadStore : 0x291cb5b9390 [Type: ThreadStore *]
...
0:015> dx -r1 ((ConsoleApp1!ThreadStore *)0x291cb5b9390)
((ConsoleApp1!ThreadStore *)0x291cb5b9390) : 0x291cb5b9390 [Type: ThreadStore *]
[+0x000] m_ThreadList [Type: SList<Thread,DefaultSListTraits<Thread,DoNothingFailFastPolicy> >]
[+0x008] m_pRuntimeInstance : 0x291cb5b9300 [Type: RuntimeInstance *]
[+0x010] m_Lock [Type: ReaderWriterLock]
0:015> dx -r1 (*((ConsoleApp1!SList<Thread,DefaultSListTraits<Thread,DoNothingFailFastPolicy> > *)0x291cb5b9390))
(*((ConsoleApp1!SList<Thread,DefaultSListTraits<Thread,DoNothingFailFastPolicy> > *)0x291cb5b9390)) [Type: SList<Thread,DefaultSListTraits<Thread,DoNothingFailFastPolicy> >]
[+0x000] m_pHead : 0x291ed366240 [Type: Thread *]
0:015> dx -r1 ((ConsoleApp1!Thread *)0x291ed366240)
((ConsoleApp1!Thread *)0x291ed366240) : 0x291ed366240 [Type: Thread *]
...
[+0x058] m_pNext : 0x291cb6aeb60 [Type: Thread *]
[+0x060] m_hPalThread : 0x204 [Type: void *]
[+0x068] m_ppvHijackedReturnAddressLocation : 0x0 [Type: void * *]
[+0x070] m_pvHijackedReturnAddress : 0x0 [Type: void *]
[+0x078] m_uHijackedReturnValueFlags : 0x0 [Type: unsigned __int64]
[+0x080] m_pExInfoStackHead : 0x0 [Type: ExInfo *]
[+0x088] m_threadAbortException : 0x0 [Type: Object *]
[+0x090] m_pThreadLocalModuleStatics : 0x291cb6aee90 [Type: void * *]
[+0x098] m_numThreadLocalModuleStatics : 0x1 [Type: unsigned int]
[+0x0a0] m_pGCFrameRegistrations : 0x0 [Type: GCFrameRegistration *]
[+0x0a8] m_pStackLow : 0xf754100000 [Type: void *]
[+0x0b0] m_pStackHigh : 0xf754200000 [Type: void *]
[+0x0b8] m_pTEB : 0xf7533ba000 : 0x0 [Type: unsigned char *]
[+0x0c0] m_uPalThreadIdForLogging : 0x2044 [Type: unsigned __int64]
[+0x0c8] m_threadId [Type: EEThreadId]
[+0x0d0] m_pThreadStressLog : 0x0 [Type: void *]
[+0x0d8] m_interruptedContext : 0x0 [Type: _CONTEXT *]
[+0x0e0] m_redirectionContextBuffer : 0x0 [Type: unsigned char *]
0:015> dx -r1 (*((ConsoleApp1!EEThreadId *)0x291ed366308))
(*((ConsoleApp1!EEThreadId *)0x291ed366308)) [Type: EEThreadId]
[+0x000] m_uiId : 0x2044 [Type: unsigned __int64]
从CLR 的 Thread 维护的信息来看,这个结构体已经很小了,也说明 AOT 在Thread信息维护上做了很多的精简.
总的来说,AOT 确实能加速程序的初始启动,一体化的打包机制也非常方便部署,但怎么变终究还是一个托管程序,需要底层的 C++ 托着它,扛 反编译 无从谈起,所以防小人的话,该加壳的加壳,该混淆的混淆.
最后此篇关于.NET7的AOT到底能不能扛反编译?的文章就讲到这里了,如果你想了解更多关于.NET7的AOT到底能不能扛反编译?的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
创建使用.NET框架的asp.net页面时,访问该页面的客户端是否需要在其计算机上安装.NET框架? IE。用户访问www.fakesite.com/default.aspx,如果他们没有安装框架,他
我阅读了很多不同的博客和 StackOverflow 问题,试图找到我的问题的答案,但最后我找不到任何东西,所以我想自己问这个问题。 我正在构建一个应用程序,其中有一个长时间运行的工作线程,它执行一些
已锁定。这个问题及其答案是locked因为这个问题是题外话,但却具有历史意义。目前不接受新的答案或互动。 我一直想知道为什么微软为这样一个伟大的平台选择了一个如此奇怪的、对搜索引擎不友好的名称。他们就
.Net Framework .Net .NET Standard的区别 1、.NET Framework 在未来.NET Framework或许成为过去时,目前还是有很多地方在使用的。这一套
如果有选择的话,您会走哪条路? ASP.NET Webforms + ASP.NET AJAX 或 ASP.NET MVC + JavaScript Framework of your Choice
我有一个 Web 服务,它通过专用连接通过 https 使用第三方 Web 服务,我应用了 ServicePointManager.ServerCertificateValidationCallbac
为什么我应该选择ASP.NET Web Application (.NET Framework)而不是ASP.NET Core Web Application (.NET Framework)? 我在
我在网络上没有找到任何关于包含 .NET Standard、.NET Core 和 .NET Framework 项目的 .NET 解决方案的公认命名约定。 就我而言,我们在 .NET 框架项目中有以
.NET Compact 是 .NET 的完美子集吗? 假设我考虑了屏幕大小和其他限制并避免了 .NET Compact 不支持的类和方法,或者 .NET Compact 是一个不同且不兼容的 GUI
我已经阅读了所有我能找到的关于 connectionManagement 中的 maxconnection 设置的文章:即 http://support.microsoft.com/kb/821268
我现在正在使用asp.net mvc,想知道使用内置的Json或 Json.Net哪个是更好的选择,但我不确定一个人是否比另一个人有优势。 另外,如果我确实选择沿用Json.Net的路线,那么我应该选
在 Visual Studio 中,您至少可以创建三种不同类型的类库: 类库(.NET Framework) 类库(.NET 标准) 类库(.NET Core) 虽然第一个是我们多年来一直使用的,但我
.NET 和 ASP.NET 之间有什么区别?它们有什么关系? 最佳答案 ASP.Net 基于 .Net 框架构建,提供有关 Web 开发的附加功能。 你可以去看看wikipedia article
在安装更高版本(3.0)之前,我需要安装.net框架1.1和2.0吗?或者单独安装 3.0 框架就足够了,并为在早期框架版本上编写的软件提供支持?谢谢 ,丽然 最佳答案 不,您不必安装以前的框架。 我
我正在开发一个项目,人们可以“更新”类别,例如更改类别的名称。我收到以下消息 This is called after clicking update 按钮 with the SQL statemen
.NET 类 System.Net.CookieContainer 线程安全吗? --更新:交 key 答复-- 是否有任何方法可以确保异步请求期间修改的变量(即 HttpWebRequest.Coo
我正在使用 JScript.NET 在我编写的 C# WinForms 应用程序中编写脚本。它工作得很好,但我只是尝试在脚本中放置一些异常处理,但我无法弄清楚如何判断我的 C# 代码抛出了哪种类型的异
我需要你的帮助, 比如我有一个小数类型的变量,我想这样取整。 例如 3.0 = 3 3.1 = 4 3.2 = 4 3.3 = 4 3.4 = 4 3.5 = 4 3.6 = 4 3.7 = 4 3.
我使用过这样的代码:http://msdn.microsoft.com/en-us/library/dw70f090.aspx在 ASP.NET 中工作之前访问数据库(2-3 年前)。我没有意识到我正
自 ConfigurationManager .NET Standard 中不存在,检索正在执行的程序集的应用程序设置的最佳方法是什么,无论是 web.config或 appSettings.{env
我是一名优秀的程序员,十分优秀!