枚举和移除进程和线程回调

进程回调可以监视进程的创建和退出,这个在前面的章节已经讲过了。 某些游戏保护的
驱动喜欢用这个函数来监视有没有黑名单中的程序运行,如果运行则阻止运行或者把游戏退
出。而线程回调则通常用来监控远程线程的建立,如果发现有远程线程注入到了游戏进程里,
则马上把游戏退出。

我们注册的进程回调,会存储在一个名为 PspCreateProcessNotifyRoutine 的数组里。
PspCreateProcessNotifyRoutine 可以理解成一个 PVOID 数组, 它记录了系统里所有进程回调
的地址。这个数组最大长度是 64*sizeof(PVOID)。 所以枚举进程回调的思路如下:找到这个
数组的地址, 然后解密数组的数据, 得到所有回调的地址(这个数组记录的数据并不是回调
的 地 址 , 而 是 经 过 加 密 地 址 , 需 要 解 密 才 行 )。 枚 举 线 程 回 调 同 理 , 要 找 到
PspCreateThreadNotifyRoutine 的地址(这个数组最大长度也是 64*sizeof(PVOID)),然后解密
数据,并把解密后的地址打印出来。

至于怎么处理这些回调就简单了。可以使用标准函数(PsSetCreateProcessNotifyRoutine、
PsRemoveCreateThreadNotifyRoutine) 将其摘掉, 也可以直接在回调函数首地址写入 RET 把
回调函数废掉。 WIN64AST 就提供了两种办法处理,以对付某些奸诈的游戏保护。
首先要获得 PspCreateProcessNotifyRoutine 的地址。 PspCreateProcessNotifyRoutine 在
PspSetCreateProcessNotifyRoutine 函数里出现了。 而 PspSetCreateProcessNotifyRoutine 则在
PsSetCreateProcessNotifyRoutine 中被调用(注意前一个是 PspXXX, 后一个是 PsXXX)。 找到
PspSetCreateProcessNotifyRoutine 之后,再匹配特征码

#include <ntddk.h>


void CreateThreadNotify1
(
IN HANDLE  ProcessId,
IN HANDLE  ThreadId,
IN BOOLEAN  Create
)
{
	DbgPrint("CreateThreadNotify1\n");
}

void CreateThreadNotify2
(
IN HANDLE  ProcessId,
IN HANDLE  ThreadId,
IN BOOLEAN  Create
)
{
	DbgPrint("CreateThreadNotify2\n");
}

void CreateThreadNotifyTest(BOOLEAN Remove)
{
	if (!Remove)
	{
		PsSetCreateThreadNotifyRoutine(CreateThreadNotify1);
		PsSetCreateThreadNotifyRoutine(CreateThreadNotify2);
	}
	else
	{
		PsRemoveCreateThreadNotifyRoutine(CreateThreadNotify1);
		PsRemoveCreateThreadNotifyRoutine(CreateThreadNotify2);
	}
}

ULONG64 FindPspCreateProcessNotifyRoutine()
{
	LONG			OffsetAddr = 0;
	ULONG64			i = 0, pCheckArea = 0;
	UNICODE_STRING	unstrFunc;
	//获得PsSetCreateProcessNotifyRoutine的地址
	RtlInitUnicodeString(&unstrFunc, L"PsSetCreateProcessNotifyRoutine");
	pCheckArea = (ULONG64)MmGetSystemRoutineAddress(&unstrFunc);
	//获得PspSetCreateProcessNotifyRoutine的地址
	memcpy(&OffsetAddr, (PUCHAR)pCheckArea + 4, 4);
	pCheckArea = (pCheckArea + 3) + 5 + OffsetAddr;
	DbgPrint("PspSetCreateProcessNotifyRoutine: %llx", pCheckArea);
	//获得PspCreateProcessNotifyRoutine的地址
	for (i = pCheckArea; i<pCheckArea + 0xff; i++)
	{
		if (*(PUCHAR)i == 0x4c && *(PUCHAR)(i + 1) == 0x8d && *(PUCHAR)(i + 2) == 0x35)	//lea r14,xxxx
		{
			LONG OffsetAddr = 0;
			memcpy(&OffsetAddr, (PUCHAR)(i + 3), 4);
			return OffsetAddr + 7 + i;
		}
	}
	return 0;
}

//
VOID EnumCreateProcessNotify()
{
	int i = 0;
	BOOLEAN b;
	ULONG64	NotifyAddr = 0, MagicPtr = 0;
	ULONG64	PspCreateProcessNotifyRoutine = FindPspCreateProcessNotifyRoutine();
	DbgPrint("PspCreateProcessNotifyRoutine: %llx", PspCreateProcessNotifyRoutine);
	if (!PspCreateProcessNotifyRoutine)
		return;
	for (i = 0; i<64; i++)
	{
		MagicPtr = PspCreateProcessNotifyRoutine + i * 8;
		NotifyAddr = *(PULONG64)(MagicPtr);
		if (MmIsAddressValid((PVOID)NotifyAddr) && NotifyAddr != 0)
		{
			NotifyAddr = *(PULONG64)(NotifyAddr & 0xfffffffffffffff8);
			DbgPrint("[CreateProcess]%llx", NotifyAddr);
		}
	}
}

ULONG64 FindPspCreateThreadNotifyRoutine()
{
	ULONG64			i = 0, pCheckArea = 0;
	UNICODE_STRING	unstrFunc;
	RtlInitUnicodeString(&unstrFunc, L"PsSetCreateThreadNotifyRoutine");
	pCheckArea = (ULONG64)MmGetSystemRoutineAddress(&unstrFunc);
	DbgPrint("PsSetCreateThreadNotifyRoutine: %llx", pCheckArea);
	for (i = pCheckArea; i<pCheckArea + 0xff; i++)
	{
		if (*(PUCHAR)i == 0x48 && *(PUCHAR)(i + 1) == 0x8d && *(PUCHAR)(i + 2) == 0x0d)	//lea rcx,xxxx
		{
			LONG OffsetAddr = 0;
			memcpy(&OffsetAddr, (PUCHAR)(i + 3), 4);
			return OffsetAddr + 7 + i;
		}
	}
	return 0;
}


VOID EnumCreateThreadNotify()
{
	int i = 0;
	BOOLEAN b;
	ULONG64	NotifyAddr = 0, MagicPtr = 0;
	ULONG64	PspCreateThreadNotifyRoutine = FindPspCreateThreadNotifyRoutine();
	DbgPrint("PspCreateThreadNotifyRoutine: %llx", PspCreateThreadNotifyRoutine);
	if (!PspCreateThreadNotifyRoutine)
		return;
	for (i = 0; i<64; i++)
	{
		MagicPtr = PspCreateThreadNotifyRoutine + i * 8;
		NotifyAddr = *(PULONG64)(MagicPtr);
		if (MmIsAddressValid((PVOID)NotifyAddr) && NotifyAddr != 0)
		{
			NotifyAddr = *(PULONG64)(NotifyAddr & 0xfffffffffffffff8);
			DbgPrint("[CreateThread]%llx", NotifyAddr);
		}
	}
}

 

<think> 我们讨论的是RecyclerView及其Adapter在异步渲染完成后的回调问题。需要确保在异步加载数据并设置Adapter后,能够准确监听到所有视图渲染完成。 之前的讨论中已经给出了几种方法,包括使用ViewTreeObserver、自定义LayoutManager、结合Adapter数据观察等。 现在用户的问题更侧重于“异步渲染”,即数据加载是异步的,设置Adapter也可能在后台线程(但UI操作必须在主线程),渲染本身是异步的过程。 我们可以提供一种方案,结合数据加载的异步性渲染完成的检测,确保在数据设置后,RecyclerView完成所有item的布局后再回调。 注意:由于RecyclerView的渲染是异步的,所以我们需要在数据设置后监听布局完成。 这里我们采用一种组合方案: 1. 在异步任务中加载数据。 2. 回到主线程设置Adapter(这步必须在主线程)。 3. 注册一个全局布局监听器(ViewTreeObserver.OnGlobalLayoutListener)来检测布局完成。 4. 在布局完成的回调中,我们判断是否有子项(或者为空)来触发渲染完成的回调。 同时,为了避免布局监听器被多次调用,我们在第一次回调后立即移除监听。 另外,考虑异步加载数据可能很快,但渲染需要时间,所以我们需要确保在视图布局完成后再回调。 此外,我们还需要考虑数据为空的情况。 以下是示例代码: </think> ### RecyclerView 与 Adapter 异步渲染回调解决方案 在 Android 开发中实现 RecyclerView 与 Adapter 的异步渲染回调,关键在于协调数据加载、视图绑定渲染完成的监听时序。以下是完整实现方案: #### 核心架构:异步数据流 + 渲染状态机 ```kotlin class AsyncRenderRecyclerView(context: Context) : RecyclerView(context) { // 渲染状态枚举 enum class RenderState { IDLE, DATA_LOADING, ADAPTER_SETTING, RENDERING, COMPLETED } private var currentState = RenderState.IDLE private val renderListeners = mutableListOf<() -> Unit>() private val mainHandler = Handler(Looper.getMainLooper()) // 注册渲染完成监听器 fun addRenderListener(listener: () -> Unit) { if (currentState == RenderState.COMPLETED) { listener.invoke() // 若已完成立即回调 } else { renderListeners.add(listener) } } // 异步设置适配器(核心方法) fun setAdapterAsync(adapter: Adapter<*>, dataLoader: () -> List<Any>) { currentState = RenderState.DATA_LOADING // 启动异步数据加载 CoroutineScope(Dispatchers.IO).launch { val data = dataLoader() withContext(Dispatchers.Main) { currentState = RenderState.ADAPTER_SETTING this@AsyncRenderRecyclerView.adapter = adapter.apply { submitList(data) // 提交数据 } // 启动渲染监听 startRenderingMonitor() } } } private fun startRenderingMonitor() { currentState = RenderState.RENDERING // 方案1:视图树布局监听 viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener { override fun onGlobalLayout() { viewTreeObserver.removeOnGlobalLayoutListener(this) checkRenderCompletion() } }) // 方案2:适配器数据观察(双保险) adapter?.registerAdapterDataObserver(object : AdapterDataObserver() { override fun onItemRangeInserted(position: Int, itemCount: Int) { super.onItemRangeInserted(position, itemCount) checkRenderCompletion() } }) // 超时保护(2秒) mainHandler.postDelayed({ if (currentState != RenderState.COMPLETED) { notifyRenderCompletion() } }, 2000) } private fun checkRenderCompletion() { post { // 双重验证条件 val isRendered = (childCount > 0 && getChildAt(0).width > 0) || (adapter?.itemCount == 0) if (isRendered) notifyRenderCompletion() } } private fun notifyRenderCompletion() { if (currentState == RenderState.COMPLETED) return currentState = RenderState.COMPLETED // 通知所有监听器 renderListeners.forEach { it.invoke() } renderListeners.clear() } } ``` #### 使用示例 ```kotlin // 在 Activity/Fragment 中 val asyncRecyclerView = AsyncRenderRecyclerView(context).apply { layoutManager = LinearLayoutManager(context) // 添加渲染监听 addRenderListener { Log.d("Render", "异步渲染完成") hideLoading() } // 异步设置适配器 setAdapterAsync(MyAdapter()) { // 模拟耗时数据加载(IO线程执行) delay(1000) generateLargeDataList() // 返回数据集 } } ``` #### 关键优化技术 1. **异步管道控制** ```kotlin // 使用协程管道控制并发 val renderChannel = Channel<RenderEvent>(capacity = 10) suspend fun processRenderEvents() { for (event in renderChannel) { when (event) { is DataLoadedEvent -> handleDataLoaded(event.data) is ViewRenderedEvent -> handleViewRendered() } } } ``` 2. **视图渲染验证** ```kotlin // 精确检测 item 完成渲染 private fun isViewRendered(view: View): Boolean { return view.width > 0 && view.height > 0 && ViewCompat.isLaidOut(view) } // 在 LayoutManager 中覆盖 override fun onLayoutCompleted(state: State?) { super.onLayoutCompleted(state) if (findFirstVisibleItemPosition() != -1) { asyncRecyclerView?.checkRenderCompletion() } } ``` 3. **性能监控工具** ```kotlin // 添加渲染时序标记 class RenderTrace { fun startTrace(tag: String) = Trace.beginSection(tag) fun endTrace() = Trace.endSection() } // 在关键节点添加 renderTrace.startTrace("ASYNC_DATA_LOAD") // ...数据加载代码 renderTrace.endTrace() ``` ### 异常处理机制 1. **内存泄漏防护** ```kotlin // 在 onDestroy 中释放资源 override fun onDestroy() { asyncRecyclerView?.apply { clearRenderListeners() mainHandler.removeCallbacksAndMessages(null) } } ``` 2. **错误重试策略** ```kotlin private var retryCount = 0 private fun handleRenderError() { if (retryCount++ < MAX_RETRY) { postDelayed({ startRenderingMonitor() }, 300) } else { notifyRenderCompletion() // 强制完成 } } ``` 3. **并发冲突解决** ```kotlin // 使用原子状态更新 private val renderState = AtomicReference(RenderState.IDLE) fun updateState(newState: RenderState) { while (true) { val current = renderState.get() if (renderState.compareAndSet(current, newState)) break } } ``` ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值