【185】Java8结合C++读取和杀死windows10进程,使用JNI技术

11 篇文章 1 订阅

Java8 要想读取和杀死进程,通过Java代码自身是无法搞定的,必须要利用JNI结合C++来做这个事情。

整个文章的代码在 https://gitee.com/zhangchao19890805/csdnBlog 上。克隆代码后进入blog185就是本文代码,其中 blog185vc 是C++代码,blog185java 是Java代码。

整个过程分成如下几步:

第一步:编写Java代码,预先定义 native 方法。

编写 ProcessDto 类,用于 C++ 向 Java 传递进程信息。

package zhangchao;

/**
 * 传递进程信息的类
 * @author zhangchao
 */
public class ProcessDto {
    // 进程id
    private Integer pid;

    // 进程名称
    private String name;

    // 进程开始时间
    private String startDateStr;

    // 命令行参数
    private String cmdline;

    ///
    //  setters/getters
    //


    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("ProcessDto{");
        sb.append("pid=").append(pid);
        sb.append(", name='").append(name).append('\'');
        sb.append(", startDateStr='").append(startDateStr).append('\'');
        sb.append(", cmdline='").append(cmdline).append('\'');
        sb.append('}');
        return sb.toString();
    }

    public Integer getPid() {
        return pid;
    }

    public void setPid(Integer pid) {
        this.pid = pid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getStartDateStr() {
        return startDateStr;
    }

    public String getCmdline() {
        return cmdline;
    }

    public void setCmdline(String cmdline) {
        this.cmdline = cmdline;
    }

    public void setStartDateStr(String startDateStr) {
        this.startDateStr = startDateStr;
    }
}

编写调用 JNI 的 ZcProcessJni 类

package zhangchao;

import java.util.ArrayList;
import java.util.List;

/**
 * 调用JNI的类
 *
 * cd 进入D:\ws\csdnBlog\blog185\blog185java\target\classes>
 * javah -jni zhangchao.ZcProcessJni  获取 zhangchao_ZcProcessJni.h 文件
 *
 * javap -s -p zhangchao.ZcProcessJni 获取 Java 方法的信息
 *
 * Compiled from "ZcProcessJni.java"
 * public class zhangchao.ZcProcessJni {
 *   public static java.lang.String ERROR_LOAD_MESSAGE;
 *     descriptor: Ljava/lang/String;
 *   public java.util.List<zhangchao.ProcessDto> processList;
 *     descriptor: Ljava/util/List;
 *   public zhangchao.ZcProcessJni();
 *     descriptor: ()V
 *
 *   public native void lookProcess();
 *     descriptor: ()V
 *
 *   public native void killProcess(int);
 *     descriptor: (I)V
 *
 *   private void callback(int, java.lang.String, java.lang.String, java.lang.String);
 *     descriptor: (ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
 *
 *   static {};
 *     descriptor: ()V
 * }
 *
 *
 *
 * @author zhangchao
 *
 */
public class ZcProcessJni {
    public static String ERROR_LOAD_MESSAGE = null;

    // 存放若干进程信息
    public List<ProcessDto> processList = new ArrayList<>();

    /**
     * 查找所有进程信息
     */
    public native void lookProcess();

    /**
     * 杀死进程ID指定的进程
     * @param pid 进程ID
     */
    public native void killProcess(int pid);

    /**
     * C++ 调用 Java 的回调方法。用于返回查找到的进程信息。
     * @param pid  进程ID
     * @param name  进程名称
     * @param startDateStr 进程启动时间
     * @param cmdline  进程的命令行命令
     */
    private void callback(int pid, String name, String startDateStr, String cmdline) {
        ProcessDto dto = new ProcessDto();
        dto.setPid(pid);
        dto.setName(name);
        dto.setStartDateStr(startDateStr);
        dto.setCmdline(cmdline);
        processList.add(dto);
    }

    static {
        try {
            // 加载dll文件
            System.loadLibrary("blog185vc");
        } catch (Throwable e) {
            StringBuilder sb = new StringBuilder();
            StackTraceElement[] stackTraceElements = e.getStackTrace();
            if (null != stackTraceElements) {
                for (StackTraceElement stackTraceElement : stackTraceElements) {
                    sb.append(stackTraceElement).append("\n");
                }
            }
            ERROR_LOAD_MESSAGE = sb.toString();
        }

    }

}

Main 类包含 main 方法,查找视频软件 VLC 的进程,打印信息后杀死进程。

package zhangchao;

public class Main {
    public static void main(String[] args) {
        System.out.println("begin");
        if (ZcProcessJni.ERROR_LOAD_MESSAGE != null &&
                ZcProcessJni.ERROR_LOAD_MESSAGE.length() > 0) {
            System.out.println("Error: " + ZcProcessJni.ERROR_LOAD_MESSAGE);
            return;
        }
        ZcProcessJni zcProcessJni = new ZcProcessJni();
        zcProcessJni.lookProcess();
        if (null != zcProcessJni.processList && !zcProcessJni.processList.isEmpty()) {
            for (ProcessDto item : zcProcessJni.processList) {
                // 查找到视频播放软件VLC的进程,就打印出进程信息,并且杀死进程。
                if ("vlc.exe".equalsIgnoreCase(item.getName())) {
                    System.out.println(item);
                    zcProcessJni.killProcess(item.getPid());
                }
            }
        }
        System.out.println("end");
    }
}

第二步,利用 Java 工具为编写 C++ 代码做好准备。

编写完 Java 代码后,用 maven 的 compile 功能生成 class 文件。然后进入命令行窗口,通过 cd 命令进入 target/classes 文件夹。

执行如下命令:

javah -jni zhangchao.ZcProcessJni

classes 文件夹中会生成 zhangchao_ZcProcessJni.h 文件。

接着执行如下命令:

D:\ws\csdnBlog\blog185\blog185java\target\classes>javap -s -p zhangchao.ZcProcessJni
Compiled from "ZcProcessJni.java"
public class zhangchao.ZcProcessJni {
  public static java.lang.String ERROR_LOAD_MESSAGE;
    descriptor: Ljava/lang/String;
  public java.util.List<zhangchao.ProcessDto> processList;
    descriptor: Ljava/util/List;
  public zhangchao.ZcProcessJni();
    descriptor: ()V

  public native void lookProcess();
    descriptor: ()V

  public native void killProcess(int);
    descriptor: (I)V

  private void callback(int, java.lang.String, java.lang.String, java.lang.String);
    descriptor: (ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V

  static {};
    descriptor: ()V
}

保存好 callback 方法的描述符 (ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V ,这在 C++ 代码中会用到。

去 Java 的安装路径找到对应的 h 文件。以我电脑为例,我在 D:\program\Java\jdk1.8.0_291\include 找到 jni.h 文件。

D:\program\Java\jdk1.8.0_291\include\win32 找到 jni_md.h 文件。

第三步,编写 C++

打开 Visual Studio ,创建 DLL项目。项目名是 blog185vc。把 jni.h、jni_md.h、zhangchao_ZcProcessJni.h 三个文件放到 D:\ws\csdnBlog\blog185\blog185vc\lib\header 文件夹中。Visual Studio中点击【调试】,点击 blog185vc 属性调整。 在打开的对话窗中,左侧点击 【C/C++】,再点击【常规】。点击【附加目录包含】右侧向下箭头,再点击编辑,在弹出的对话窗中填写 D:\ws\csdnBlog\blog185\blog185vc\lib\header。

由于C++ 代码比较多,本文只写主要逻辑代码,读者可以阅读GIT中更加详细的代码。

#include <stdio.h>
#include<string.h>
#include <fstream>
#include "pch.h"
#include "jni.h"
#include "zhangchao_ZcProcessJni.h"
#include <windows.h>
#include "errhandlingapi.h"
#include <tlhelp32.h>	//进程快照函数头文件
#include <stringapiset.h>
#include<tchar.h>
#include<Winternl.h>
#include "StringBuilder.h"
#include "Logger.h"

using namespace std;


typedef NTSTATUS(WINAPI* QT)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);//NtQueryInformationProcess

/*
写成一个函数,来获得所有的PEB结构体信息
*/
char* GetProcessCommandLine(HANDLE hProcess)
{
	HMODULE hModule = 0;
	QT NtQuery = { 0 };
	hModule = LoadLibrary(L"Ntdll.dll");  //加载动态链接
	if (hModule)
	{
		NtQuery = (QT)GetProcAddress(hModule, "NtQueryInformationProcess");
		if (NtQuery == NULL)
			return 0;
	}
	else
		return 0;
	PROCESS_BASIC_INFORMATION pi = { 0 };
	NTSTATUS re = NtQuery(hProcess,
		ProcessBasicInformation, &pi, sizeof(pi), NULL);

	/*
	在pi中,pi.PebBaseAddress指向PEB,那么这个指针指向的地址是哪个地址呢?
	这个指针指向的地址,是word进程的地址
	*/
	if (!NT_SUCCESS(re))
	{
		//	_tprintf(L"OK\n");
		return 0;
	}

	PEB peb;
	RTL_USER_PROCESS_PARAMETERS para;

	ReadProcessMemory(hProcess, pi.PebBaseAddress, &peb, sizeof(peb), NULL); //将pi.PebBaseAddress的信息读取到peb中

	ReadProcessMemory(hProcess, peb.ProcessParameters, &para, sizeof(para), NULL);

	wchar_t* CommandLine = new wchar_t[1024];
	ReadProcessMemory(hProcess, para.CommandLine.Buffer, CommandLine, 1024 * 2, NULL);


	int iSize = WideCharToMultiByte(CP_ACP, 0, CommandLine, -1, NULL, 0, NULL, NULL);
	char* cmdline = new char[iSize + 10];
	WideCharToMultiByte(CP_ACP, 0, CommandLine, -1, cmdline, iSize, NULL, NULL);
	FreeLibrary(hModule);
	return cmdline;
}


/*
 * Class:     zhangchao_ZcProcessJni
 * Method:    lookProcess
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_zhangchao_ZcProcessJni_lookProcess(JNIEnv* env, jobject obj)
{
	// 获取Java对象中方法的ID
	jclass cls = env->GetObjectClass(obj);
	jmethodID mid = env->GetMethodID(cls, "callback", "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
	if (NULL == mid) {
		return;
	}

	PROCESSENTRY32W currentProcess;						//存放快照进程信息的一个结构体
	currentProcess.dwSize = sizeof(currentProcess);		//在使用这个结构之前,先设置它的大小
	HANDLE hProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);//给系统内的所有进程拍一个快照

	if (hProcess == INVALID_HANDLE_VALUE)
	{
		printf("CreateToolhelp32Snapshot()调用失败!\n");
		return;
	}

	bool bMore = Process32FirstW(hProcess, &currentProcess);	//获取第一个进程信息
	while (bMore)
	{
		HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, currentProcess.th32ProcessID);
		if (process != NULL) {
			FILETIME ftCreation, ftExit, ftKernel, ftUser;
			SYSTEMTIME stCreation, lstCreation;
			if (GetProcessTimes(process, &ftCreation, &ftExit, &ftKernel, &ftUser)) {
				FileTimeToSystemTime(&ftCreation, &stCreation);
				SystemTimeToTzSpecificLocalTime(NULL, &stCreation, &lstCreation);

				char nameArr[256];
				int iSize = WideCharToMultiByte(CP_ACP, 0, currentProcess.szExeFile, -1, NULL, 0, NULL, NULL);
				WideCharToMultiByte(CP_ACP, 0, currentProcess.szExeFile, -1, nameArr, iSize, NULL, NULL);
				jstring nameStr = env->NewStringUTF(nameArr);

				char timeArr[128];
				sprintf_s(timeArr, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d", lstCreation.wYear, lstCreation.wMonth, lstCreation.wDay,
					lstCreation.wHour, lstCreation.wMinute, lstCreation.wSecond);
				jstring timeStr = env->NewStringUTF(timeArr);

				char* cmdline = GetProcessCommandLine(process);
				jstring jsCmdline = env->NewStringUTF(cmdline);

				env->CallVoidMethod(obj, mid, currentProcess.th32ProcessID, nameStr, timeStr, jsCmdline);
			}
			CloseHandle(process);
		}
		else {
			//wcout << "PID=" << currentProcess.th32ProcessID << "   PName=" << currentProcess.szExeFile << endl;
		}


		bMore = Process32NextW(hProcess, &currentProcess);	//遍历下一个

	}
	CloseHandle(hProcess);	//清除hProcess句柄
}



/*
 * Class:     zhangchao_ZcProcessJni
 * Method:    killProcess
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_zhangchao_ZcProcessJni_killProcess(JNIEnv* env, jobject obj, jint pid) 
{
	unique_ptr<Logger> logger(new Logger());
	unique_ptr<StringBuilder> sb{ new StringBuilder() };
	logger->info("BEGIN killProcess");

	PROCESSENTRY32W currentProcess;						//存放快照进程信息的一个结构体
	currentProcess.dwSize = sizeof(currentProcess);		//在使用这个结构之前,先设置它的大小
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);//给系统内的所有进程拍一个快照
	if (hSnapshot == INVALID_HANDLE_VALUE)
	{
		logger->info("CreateToolhelp32Snapshot()调用失败!");
		return;
	}

	bool bMore = Process32FirstW(hSnapshot, &currentProcess);	//获取第一个进程信息
	while (bMore) {
		//		HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, currentProcess.th32ProcessID);
				// 各种进程权限 https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
		HANDLE process = OpenProcess(PROCESS_TERMINATE, FALSE, currentProcess.th32ProcessID);
		if (process != NULL) {
			if (pid == currentProcess.th32ProcessID) {

				int terminateFlag = TerminateProcess(process, 0);
				if (terminateFlag) {
					logger->info(sb->clear()->append("Success TerminateProcess(process, 0);   pid=")->append(pid));
				}
				else {
					logger->info(sb->clear()->append("Fail TerminateProcess(process, 0);  pid=")->append(pid));
				}

			}
			CloseHandle(process);
		}
		else {
			logger->info(sb->clear()->append("OpenProcess failed. process == NULL.  pid=")->
				append(pid)->append("  GetLastError()=")->append(GetLastError()));
		}
		bMore = Process32NextW(hSnapshot, &currentProcess);	//遍历下一个
	}

	CloseHandle(hSnapshot);
	logger->info("END killProcess");
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值