基于java开发windows进程注入
用Java做进程注入?基于s进听起来像是个馊主意。JVM跑在虚拟机里,程注连指针都没有,基于s进怎么碰别的程注进程的内存?但真有人这么干过,而且成功了。基于s进去年我帮一个做游戏外挂检测的程注朋友调过一段代码,他用的基于s进就是Java加JNI,往目标进程里塞了一个DLL,程注用来Hook系统API。基于s进效果不赖,程注虽然启动慢得让人抓狂。基于s进 核心思路其实不复杂:Java负责逻辑,程注JNI负责脏活。基于s进你写一个native方法,程注声明成public native boolean inject(int pid),基于s进然后在C++侧实现它。C++拿到pid后,调用OpenProcess打开目标,VirtualAllocEx在对方进程里分配一块内存,WriteProcessMemory把DLL路径写进去,最后CreateRemoteThread让目标进程自己加载这个DLL。标准的Windows注入三步走,和C#、Python做的没本质区别。区别只在JNI那层桥接——你得把Java的jint转成Windows的DWORD,把jstring转成LPCWSTR,一个字节都不能错。 麻烦的地方在权限。Windows 10之后,Session隔离和ACL检查严多了。你Java程序如果跑在普通用户权限下,OpenProcess直接返回5(拒绝访问)。我测试时用的是一台Win11虚拟机,默认UAC开着,折腾了半天才发现得先提权——用ShellExecute以管理员身份重新启动Java进程,或者干脆给exe加manifest请求highestAvailable。这还没完,目标进程要是64位的,你注入的DLL也得是64位;Java这边如果跑的是32位JVM,JNI加载的native库也只能是32位,那就彻底没戏了。所以必须用64位JDK编译,再配一个64位的C++编译器,比如MSVC或者MinGW-w64。 写JNI代码时有个坑值得说。Java的String和C++的wchar_t*不是一回事,直接传jstring进去,C++侧取出来是乱码。你得用GetStringChars或者GetStringUTFChars,用完还要Release。我在一个循环注入测试里忘了Release,内存泄漏直接导致JVM崩溃,报了个EXCEPTION_ACCESS_VIOLATION,查了半天才找到原因。另一个细节是线程同步——CreateRemoteThread创建的远程线程没法直接等它结束,你得用WaitForSingleObject,但JNI调用会阻塞当前Java线程,如果你的注入逻辑跑在EDT(事件分发线程)上,界面就卡死了。解决办法是丢给一个独立的ExecutorService,异步等结果。 效果呢?我那个朋友用这套方案,把注入延迟控制在200毫秒内,Hook了NtQuerySystemInformation来隐藏特定进程。Java侧用Socket和C2服务器通信,动态下发要注入的DLL内容,写进临时文件再加载。这套东西跑在目标机器上,杀软竟然没报——因为JVM启动时加载的native库签名是合法的,远程线程的调用栈看起来也像是正常模块加载。但别高兴太早,如果目标进程有DLL加载校验(比如校验模块签名),或者开了CFG(控制流保护),你的CreateRemoteThread会直接触发异常。我试过注入一个用VS2019编译的测试程序,开了/guard:cf,结果远程线程刚跑起来就被终止了,连错误码都没给。 说到底,Java做注入不是不行,但绕的弯子比原生C++多一倍。JNI的异常处理机制糟透了——C++侧抛出的异常没法直接传到Java层,你得自己定义错误码,手动检查每个API调用的返回值。写多了你会发现,这活儿本质上是用Java的皮包着C++的心,调试时还得同时开两个IDE。但如果你像我一样,业务逻辑已经全写在Java里了,不想再维护一套独立的C++注入器,那JNI这条路值得走。至少我那个朋友的外挂检测系统,到现在还在生产环境里跑着,没出过岔子。
- 随机阅读
- 搜索
-
- 友情链接
-