🛫 系列文章导航
- 【Frida】 00_简单介绍和使用 https://blog.csdn.net/kinghzking/article/details/123225580
- 【Frida】 01_食用指南 https://blog.csdn.net/kinghzking/article/details/126849567
- 【Frida】 03_初识frida-node https://blog.csdn.net/kinghzking/article/details/136685316
- 【Frida】 04_Frida中使用TypeScript脚本(采坑) https://blog.csdn.net/kinghzking/article/details/136772475
- 【Frida】 05_读取扫雷游戏的数据 https://blog.csdn.net/kinghzking/article/details/136781623
- 【Frida】 06_分析扫雷游戏的数据,显示地雷位置 https://blog.csdn.net/kinghzking/article/details/136685316
- 【Frida】 07_让系统重新绘制指定窗口 https://blog.csdn.net/kinghzking/article/details/136829854
- 【Frida】 08_将目标窗口切换到前台 https://blog.csdn.net/kinghzking/article/details/136837275
- 【Frida】 09_获取软件窗口位置,设置鼠标指针位置 https://blog.csdn.net/kinghzking/article/details/136854052
- 【Frida】10_用鼠标自动标记棋盘上的雷区(一键过关) https://blog.csdn.net/kinghzking/article/details/136854020
- 【frida-实战】“一行”代码教你获取WeGame平台中所有的lua脚本 https://blog.csdn.net/kinghzking/article/details/125590584
- 【Frida-实战】EA游戏平台的文件监控(PsExec.exe提权) https://blog.csdn.net/kinghzking/article/details/130512479
▒ 目录 ▒
🛫 导读
开发环境
版本号 | 描述 | |
---|---|---|
文章日期 | 2024-03-17 | |
操作系统 | Win11 - 22H2 | 22621.2715 |
node -v | v20.10.0 | |
npm -v | 10.2.3 | |
yarn -v | 3.1.1 | |
frida-compile | 10.2.1 | 高版本各种异常 |
扫雷程序下载地址 | https://download.csdn.net/download/kinghzking/88979919 | |
课程源码 | https://gitcode.net/kinghzking/MyOpen | 所在目录:/course/frida |
1️⃣ 实现方案
在Windows编程中,GUI界面编程经过二三十年的发展,产生了大量API,庞杂无比,应用场景各异,这里简单记录下。
可以使用以下方法将窗口切换到前台:
SetForegroundWindow
SetForegroundWindow函数:SetForegroundWindow函数可将指定窗口设置为前台窗口。以下是一个简单的示例代码:
#include <windows.h> int main() { HWND hWnd = FindWindow(NULL, "Window Title"); if (hWnd != NULL) { SetForegroundWindow(hWnd); } return 0; }
SwitchToThisWindow
SwitchToThisWindow函数:SwitchToThisWindow函数可以将指定窗口立即切换到前台而不改变焦点。示例代码如下:
#include <windows.h> int main() { HWND hWnd = FindWindow(NULL, "Window Title"); if (hWnd != NULL) { SwitchToThisWindow(hWnd, TRUE); } return 0; }
AttachThreadInput
AttachThreadInput函数:通过AttachThreadInput函数将目标窗口所在线程的输入处理过程与当前线程的输入处理过程连接起来,从而实现将窗口切换到前台。示例代码如下:
#include <windows.h> int main() { HWND hWnd = FindWindow(NULL, "Window Title"); if (hWnd != NULL) { DWORD dwThisThreadId = GetCurrentThreadId(); DWORD dwTargetThreadId = GetWindowThreadProcessId(hWnd, NULL); AttachThreadInput(dwThisThreadId, dwTargetThreadId, TRUE); SetForegroundWindow(hWnd); AttachThreadInput(dwThisThreadId, dwTargetThreadId, FALSE); } return 0; }
SetActiveWindow
SetActiveWindow函数:使用SetActiveWindow函数可以将指定窗口设置为活动窗口(即前台窗口)。示例代码如下:
#include <windows.h> int main() { HWND hWnd = FindWindow(NULL, "Window Title"); if (hWnd != NULL) { SetActiveWindow(hWnd); } return 0; }
BringWindowToTop
BringWindowToTop函数:BringWindowToTop函数可以将指定窗口切换到Z轴的顶部,但并
不会
将窗口设置为焦点窗口
。示例代码如下:#include <windows.h> int main() { HWND hWnd = FindWindow(NULL, "Window Title"); if (hWnd != NULL) { BringWindowToTop(hWnd); } return 0; }
SetWindowPos
SetWindowPos函数:使用SetWindowPos函数可以设置窗口的位置和大小,并将其放置在Z顺序的指定位置,从而将窗口切换到前台。示例代码如下:
#include <windows.h> int main() { HWND hWnd = FindWindow(NULL, "Window Title"); if (hWnd != NULL) { SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } return 0; }
ShowWindow
ShowWindow函数:通过ShowWindow函数可以显示指定窗口,并将其设为活动窗口,从而将窗口切换到前台。示例代码如下:
#include <windows.h> int main() { HWND hWnd = FindWindow(NULL, "Window Title"); if (hWnd != NULL) { ShowWindow(hWnd, SW_SHOW); SetActiveWindow(hWnd); } return 0; }
PostMessage
PostMessage函数:通过发送WM_SYSCOMMAND消息将窗口最大化,然后再恢复窗口大小可以将窗口切换到前台。有点
不太实用
。
示例代码如下:#include <windows.h> int main() { HWND hWnd = FindWindow(NULL, "Window Title"); if (hWnd != NULL) { PostMessage(hWnd, WM_SYSCOMMAND, SC_RESTORE, 0); PostMessage(hWnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0); } return 0; }
2️⃣ 编码实现
上面讲述了大量的窗口换到前台的API,独立的API并不能保证程序的正常,这里使用部分接口
组合实现
。
kernel32.ts封装
之前我们用的都是
User32.dll
模块导出函数,现在需要引入Kernel32.dll
模块的导出函数,因此增加kernel32.ts
模块。
封装和引用,同
User32.ts
一样的,不再赘述。
生成NativeFunction封装
由于代码中大量引入
NativeFunction
对象,为了更好的编码,我们将其进行封装EZ生成NativeFunction
。
该函数用到了两个frida方法:Module.findExportByName
和new NativeFunction
,我们将这两个函数的参数全部作为我们的函数EZ生成NativeFunction
的参数,其中abiOrOptions、moduleName都有默认值,以此减少参数的传递。
最终函数如下所示:
/*
@param exportName - 导出函数名
@param retType - 返回值类型
@param argTypes - 参数类型数组
@param abiOrOptions - ABI类型或者NativeFunctionOptions类型
@param moduleName — 模块名或者路径默认为"Kernel32.dll"
*/
function EZ生成NativeFunction(exportName: string,
retType: NativeFunctionReturnType,
argTypes: [] | NativeFunctionArgumentType[],
abiOrOptions: NativeABI | NativeFunctionOptions = "default",
moduleName: string = "Kernel32.dll",
) {
let address = Module.findExportByName(moduleName, exportName);
return new NativeFunction(address!, retType, argTypes, abiOrOptions);
}
win api函数封装
我们以
GetCurrentThreadId
函数为例,说明新的函数优势:
- 静态属性
static func_GetCurrentThreadId
,保证只获取一次。- 静态win api
static GetCurrentThreadId
,方便外部模块直接调用。- 直接调用,将返回值返回,外部不再关系内部实现(完全封装了frida)。
export default class Kernel32 {
// DWORD GetCurrentThreadId();
// 静态属性,保证只获取一次。
private static func_GetCurrentThreadId: AnyFunction;
// 静态win api ,方便外部模块直接调用。
static GetCurrentThreadId(): number {
if (this.func_GetCurrentThreadId == null) {
this.func_GetCurrentThreadId = EZ生成NativeFunction("GetCurrentThreadId", "int", []);
}
// 直接调用,将返回值返回,外部不再关系内部实现(完全封装了frida)。
return this.func_GetCurrentThreadId();
}
}
其它api封装类似。针对user32中的函数,目前使用旧的方式,代码参考《https://gitcode.net/kinghzking/MyOpen》。
将目标窗口切换到前台
针对扫雷,直接调用
User32.SetWindowPos
,传参HWND_TOPMOST
就能置顶了。
但是小编又对另一款C#游戏做了测试,发现需要使用User32.AttachThreadInput
之后,再调用User32.SetWindowPos
等函数。
所以,为了保险起见,我们将各种实现混合到一起,最终代码如下所示:
将目标窗口切换到前台() {
let hForeWnd = User32.GetForegroundWindow();
let dwCurID = Kernel32.GetCurrentThreadId();
let dwForeID = User32.GetWindowThreadProcessId(hForeWnd, ptr(0));
User32.AttachThreadInput(dwCurID, dwForeID, 1);
User32.ShowWindow(this.hWnd, User32.Const.SW_RESTORE);
User32.SetForegroundWindow(this.hWnd)
User32.SetWindowPos(this.hWnd, User32.Const.HWND_TOPMOST, 0, 0, 0, 0, User32.Const.SWP_NOSIZE | User32.Const.SWP_NOMOVE);
User32.SetWindowPos(this.hWnd, User32.Const.HWND_NOTOPMOST, 0, 0, 0, 0, User32.Const.SWP_NOSIZE | User32.Const.SWP_NOMOVE);
User32.AttachThreadInput(dwCurID, dwForeID, 0);
}
🛬 文章小结
关于逆向相关的内容,很多时候,最终还是得对正向开发有更深入的理解才行。
就像本篇的内容,只有针对GUI有了一定的积累才能驾熟就轻,快速理解。
ps: 文章中内容仅用于技术交流,请勿用于违规违法行为。