windbg调试jni C++ native动态库

68 篇文章 2 订阅
9 篇文章 1 订阅

    网上有不少文章记述了如何在java程序中通过jni接口调用c/c++ native动态库,文章也的写不错,让我这个java外行人能一眼看懂。唯一可惜的就是没有关于如何调试native动态库的文章。既然如此,就由我来完成这任务。

分别贴出java和C++源码:

package compile;

public class compile 
{
	public native int Msg(String msg);
	
	public static void main(String[] args)
	{
		System.loadLibrary("JniDll");
		compile compileObj = new compile();
		compileObj.Msg("Msg");
	}
}
源码中声明了一个native方法int Msg(String msg);这个方法通过c++实现,并以dll的形式导出函数,源码如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class compile_compile */
//这段源码包含于compile_compile.h中,是javah生成的头文件
#ifndef _Included_compile_compile
#define _Included_compile_compile
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     compile_compile
 * Method:    Msg
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_compile_compile_Msg
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

#include "compile_compile.h"
//这段源码包含于compile_compile.cpp中,用于实现Java_compile_compile_Msg接口,并最终生成jniDll.dll文件
JNIEXPORT jint JNICALL Java_compile_compile_Msg
  (JNIEnv* javaEnv, jobject javaObj, jstring javaStr)
{
	const char* msgStr = javaEnv->GetStringUTFChars(javaStr,0);
	MessageBox(NULL,msgStr,"",MB_OK);

	return 0;
}
    继续开始之前,容我解释一下源码的用意:java程序先加载jniDll.dll然后调用其中的导出函数Java_compile_compile_Msg。这个导出函数的功能很简单,仅仅是弹出一个对话框。我要做的是当java解释器加载动态库jniDll.dll时,中断调试器到交互状态,使得程序员有机会为动态库中的其他源码下断点调试。

    编译生成相应的类文件和dll后,在用java解释器加载运行compile类前,必须明确被调试的目标进程。在命令行中输入命令后

C:\Users\Administrator\Desktop\studio\eclipse\compile>java -classpath ./bin;"%CL
ASSPATH%" compile.compile

cmd.exe的控制权交给java.exe,直到java.exe运行结束,才会响应新的控制台输入(如图)。


另外,compile类和jniDll.dll都不能单独成为进程运行,他们是作为java.exe的一部分被加载运行。(上图中,点击任务栏的java图标后显示jniDll.dll中的MessageBox也是一个很好的辅证)。综上所述,我们的调试目标进程是java.exe。

    调试进程确定后剩下的简单了,打开windbg-File-"Open Executable",进入Open Executable对话框:

         在"文件名"框中输入java.exe的全路径,

        "Arguments"框输入java.exe运行compile类时需要参数,本文中是:-classpath ./bin;"%CLASSPATH%" compile.compile

       "Start directory"框输入compile工程的路径,我的工程(由eclipse创建)为与C:\Users\Administrator\Desktop\studio\eclipse\compile下。

       最后点击打开,运行java.exe

    程序运行后马上会遇到初始断点,并中断到调试器。

在调试器中使能模块加载断点:

0:000> sxe ld jniDll
0:000> g #继续运行程序
继续运行程序后,调试器会因为加载jniDll事件再次被中断,此时,我们可以为windbg添加jniDll.dll的调试符号路径(生成jniDll.dll时一起生成的jniDll.pdb所在目录的路径)并下断点:

0:000:x86> .symfix C:\symbols\this  #设置调试符号路径
0:000:x86> .sympath+ C:\studio\JniDll\Debug
Symbol search path is: srv*;C:\studio\JniDll\Debug
Expanded Symbol search path is: SRV*C:\symbols\this*http://msdl.microsoft.com/download/symbols;c:\studio\jnidll\debug
0:000:x86> .reload
Reloading current modules
............................................
*** ERROR: Module load completed but symbols could not be loaded for java.exe
0:001:x86> .srcpath C:\studio\JniDll  #设置jniDll源码路径
0:000:x86> x USER32!*MessageBox* #准备在MessageBox函数下断点
758620a6 USER32!MessageBoxA = <no type information>
75864551 USER32!MessageBoxW = <no type information>
0:000:x86> bp USER32!MessageBoxA
0:000:x86> bp USER32!MessageBoxW
0:000:x86> g
*** WARNING: Unable to verify checksum for C:\Users\Administrator\Desktop\studio\eclipse\compile\JniDll.dll
ModLoad: 00000000`57d70000 00000000`57d8b000   C:\Users\Administrator\Desktop\studio\eclipse\compile\JniDll.dll #java.exe加载jniDll,触发了模块加载事件
ntdll!ZwMapViewOfSection+0xa:
00007ffe`db5e67fa c3              ret
0:001> g
Breakpoint 0 hit #遇到前面设置的断点
USER32!MessageBoxA:
758620a6 8bff            mov     edi,edi

    经过这一轮设置,我们终于可以像调试普通程序一样调试jniDll了~

0:001:x86> kb #查看函数调用堆栈
ChildEBP RetAddr  Args to Child               
0076f918 57d813b4 00000000 14ad8040 57d8553c USER32!MessageBoxA
0076fa08 024ec0c3 02129140 0076fa60 0076fa5c JniDll!Java_compile_compile_Msg+0x44 [c:\studio\jnidll\jnidll\jnidll.cpp @ 11] #调用Msg导出函数的源码函数<--
WARNING: Frame IP not in any known module. Following frames may be wrong.
0076faa8 57248bf5 0076fadc 0076fc9c 0000000a 0x24ec0c3
0076fb30 5730e0be 0076fc94 0076fba4 0076fc14 jvm!JVM_GetThreadStateNames+0x4cd75
0076fb78 57248c8e 57248a20 0076fc94 0076fba4 jvm!JVM_FindSignal+0x635ce
0076fb98 571caca7 0076fc94 14830238 02129000 jvm!JVM_GetThreadStateNames+0x4ce0e
0076fc5c 571d323f 02129140 14b759a8 14b759a8 jvm!JNI_GetCreatedJavaVMs+0x6ea7
0076fcb4 0019228d 02129140 0212a214 14b759a8 jvm!JNI_GetCreatedJavaVMs+0xf43f
0076fd00 0019ae9f 02129140 6ebad725 00000000 java+0x228d
0076fd38 0019af29 00000000 0076fd50 75cb495d java+0xae9f
0076fd44 75cb495d 02132ec0 0076fd94 77ae98ee java+0xaf29
0076fd50 77ae98ee 02132ec0 19186b7e 00000000 KERNEL32!BaseThreadInitThunk+0xe
0076fd94 77ae98c4 ffffffff 77ade0fd 00000000 ntdll_77aa0000!__RtlUserThreadStart+0x20
0076fda4 00000000 0019aec5 02132ec0 00000000 ntdll_77aa0000!_RtlUserThreadStart+0x1b





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值