0%

JAVA JNI 执行命令与调试

JAVA JNI 执行命令与调试

执行命令

代码基本抄自自园长的demo(为了篇幅省略了作者信息,这里统一说明一下~)

JNI的好处在于底层,实战中可用绕过一些waf拦截吧。

CommandExecution.java

1
2
3
public class CommandExecution {
public static native String exec(String cmd);
}

编译CommandExecution,并生成native层c代码需要用的头文件

1
2
javac -cp . CommandExecution.java
javah -cp . CommandExecution

CommandExecution.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <stdlib.h>
#include <cstring>
#include <string>
#include "CommandExecution.h"

using namespace std;

JNIEXPORT jstring

JNICALL CommandExecution_exec
(JNIEnv *env, jclass jclass, jstring str) {

if (str != NULL) {
jboolean jsCopy;
// 将jstring参数转成char指针
const char *cmd = env->GetStringUTFChars(str, &jsCopy);

// 使用popen函数执行系统命令
FILE *fd = popen(cmd, "r");

if (fd != NULL) {
// 返回结果字符串
string result;

// 定义字符串数组
char buf[128];

// 读取popen函数的执行结果
while (fgets(buf, sizeof(buf), fd) != NULL) {
// 拼接读取到的结果到result
result +=buf;
}

// 关闭popen
pclose(fd);

// 返回命令执行结果给Java
return env->NewStringUTF(result.c_str());
}

}

return NULL;
}

编译,注意这里为了后续能够调试需要加 -g 参数

1
g++ -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -g -o libcmd.so CommandExecution.cpp

MainTest.java

1
2
3
4
5
6
7
public class MainTest {
public static void main(String[] args) {
System.load("/home/anonymous/Desktop/learnjni/libcmd.so");
CommandExecution commandExecution = new CommandExecution();
System.out.println(commandExecution.exec("whoami"));
}
}
1
2
javac MainTest.java
java MainTest

调试

调试需要同时安装JetBrain家的idea和clion,并且clion要以管理员权限(sudo)启动。

  • 在调用native层的地方打断点
  • 通过jps找到所运行的java类对应的类的编号
  • 在clion选择Run->Attach to Process 然后attach到响应编号上
  • 在下断点地方步入

步过findNative后就会跳转到clion里面然后就可以愉快的调试了~

参考

recipeNoD002 - Debugging JNI code with IntelliJ/CLion
IntellJ IDEA中JNI单步调试指南
JNI安全基础