正如Android调用JNI本地方法经过有点改变章所说跟踪代码是可行的,但是跟踪某些代码会出现anr,点击取消,还是不好运,有提高办法吗?回答是有(gdb还没试过,本文只讨论ida)。
下面是我使用 0 * Message("%s = %d\n", GetString(Dword(R2+0x10),-1, ASCSTR_C), R2+0x20)打出的记录
"unlockCanvasAndPost"
"native_computeBounds"
"lockCanvasNative"
"nativeDraw"
"native_getClipBounds"
"native_drawText"
"nativeDraw"
"unlockCanvasAndPost"
"native_computeBounds"
"lockCanvasNative"
"nativeDraw"
"native_getClipBounds"
"native_drawText"
"nativeDraw"
....
反复调用然后anr了。
为了改善这种情况。经过仔细查阅IDA文档Edit breakpoint一章,发现
Low level condition:
Evaluate the condition on the remote computer. Such conditions are
faster, especially during remote debugging, because there is no
network traffic between IDA and the remote computer on each
breakpoint hit. More details
低级条件,在远程计算机计算条件。这种条件运行更快,特别是在远程调试的时候。详细内容如下:
Low level breakpoint conditions
Low level breakpoint conditions can be used to speed up the debugger. They are evaluated like this:
- for remote debugging, such a condition is evaluated on the remote computer. The following actions are bypassed: - copying the breakpoint event to the local computer - switching from debthread to the main thread - updating internal IDA structures and caches - updating the screen- for local debugging, such a condition is evaluated at low level. The following actions are bypassed: - switching from debthread to the main thread - updating internal IDA structures and caches - updating the screenIn both cases, there is a significant speed up. This improvement imposes some limitations on the breakpoint condition:
- only IDC expressions can be used for low level conditions - only functions marked as 'thread-safe' may be called - only entire registers can be accessed (e.g. EAX is ok but AL is not)Essentially this means that the only available functions are:
- read/write process registers - read/write process memory - file i/o - auxiliary string and object functions - Message() function (for debugging the breakpoint conditions)Low level breakpoint conditions are available only for Win32, WinCE, Linux, Mac, Android debuggers.
从中看到对我有影响的就是使用的函数必须带有'thread-safe'字样提示。
诸如前文使用的
0 * print(GetString(DbgDword(R2+0x10),-1, ASCSTR_C))
"method" == GetString(DbgDword(R2+0x10),-1, ASCSTR_C)
0 * Message("%s = %d\n", GetString(DbgDword(R2+0x10),-1, ASCSTR_C), R2+0x20)
其中DbgDword就是线程安全的,而Dword就不是,如此
DbgDword
// Get value of program double word (4 bytes) using the debugger memory // ea - linear address // returns: the value of the double word. Throws an exception on failure. // Thread-safe function (may be called only from the main thread and debthread)long DbgDword (long ea);
表达式中另一个函数也不行
GetString
// Get string contents // ea - linear address // len - string length. -1 means to calculate the max string length // type - the string type (one of ASCSTR_... constants) // Returns: string contents or empty stringstring GetString(long ea, long len, long type);
See also GetStringType function.
就没有
所以报错,不允许。
为了找到替代,找到一大圈无果。直到一个一个比较,先比较前几个吧如案例
可以在这个论坛下载2014攻防对抗挑战赛
#include <sys/types.h> #include <signal.h> #include <stdio.h> #include <unistd.h> #include <dlfcn.h> #include <string.h> #include <errno.h> /* package com.crackme; public class MainActivity{ private native String crackme(String paramString1, String paramString2); } Native的对应函数名要以“Java_”开头,后面依次跟上Java的“package名”、“class名”、“函数名”,中间以下划线“_” 分割,在package名中的“.”也要改为“_”。 此外,关于函数的参数和返回值也有相应的规则。对于Java中的基本类型如int 、double 、char等,在Native端都有相对应的类型来表示,如jint 、jdouble 、jchar 等;其他的对象类型则统统由jobject 来表示(String 是个例外,由于其使用广泛,故在Native代码中有 jstring 这个类型来表示,正如在上例中返回值String 对应到Native代码中的返回值jstring )。而对于Java中的数组,在Native中由jarray 对应,具体到基本类型和一般对象类型的数组则有jintArray 等和jobjectArray 分别对应(String 数组在这里没有例外,同样用jobjectArray 表示)。 还有一点需要注意的是,在JNI的Native函数中,其前两个参数JNIEnv *和jobject 是必需的——前者是一个JNIEnv 结构体的指针是JNI的核心数据,这个结构体中定义了很多JNI的接口函数指针,使开发者可以使用JNI所定义的接口功能;后者指代的是调用这个JNI函数的Java对象,有点类似于C++中的this 指针。在上述两个参数之后,还需要根据Java端的函数声明依次对应添加参数。 在上例中,Java中声明的JNI函数对应命名为: //Class: com_crackme_MainActivity //Method: crackme //Signature: (Ljava/lang/String;)Ljava/lang/String; jstring Java_com_crackme_MainActivity_crackme(JNIEnv *,jobject,jstring,jstring); jstring (*crackme)(JNIEnv *,jobject,jstring, jstring) = NULL; //事先把libcrackme.so放到root/system/lib/目录下 void *filehandle = dlopen("/system/lib/libcrackme.so", RTLD_LAZY); //(jstring (*)(JNIEnv *,jobject, jstring, jstring)) if(filehandle) { crackme = (jstring (*)(JNIEnv *,jobject, jstring, jstring))dlsym(filehandle, "Java_com_crackme_MainActivity_crackme"); if(crackme){ jstring s = crackme(env, obj, a, b); } dlclose(filehandle); filehandle = NULL; } */ typedef void *CRACKME; //typedef jstring *CRACKME(JNIEnv *,jobject, jstring, jstring); int main(int argc, char **argv) { CRACKME *crackme; int i = 0; void *handle; handle = dlopen("/home/Sansan/a/libcrackme.so", RTLD_LAZY); if (!handle) { printf("%s, %d, NULL == handle. errno = %d, %s\n", __FUNCTION__, __LINE__, errno, strerror(errno)); return -1; } crackme = dlsym(handle, "JNI_OnLoad"); if (!crackme) { printf("%s, %d, NULL == crackme\n", __FUNCTION__, __LINE__); return -1; } printf("%s, %d, crackme = %p\n", __FUNCTION__, __LINE__, crackme); dlclose(handle); return 0; }
条件语句类似这样
'c' == DbgByte(DbgDword(R2+0x10)) && 'r' == DbgByte(1+DbgDword(R2+0x10)) && 'a' == DbgByte(2+DbgDword(R2+0x10)) && 'c' == DbgByte(3+DbgDword(R2+0x10)) && 'k' == DbgByte(4+DbgDword(R2+0x10))
char *crac = "crac" 是4字节可以计算成一个4字节的整数,可能更好,类似这样计算出它在内存中的值0x63617263。
;int crac = *(int*)"crackme我啊h"; 011EF305 mov eax,dword ptr ds:[011F46BCh] 011EF30A mov dword ptr [crac],eax
跳到目标,如果F5不行需要弄一下。
最后F5结果,有神奇F5就是容易点啊,虽说君子善假于物也,依靠工具会产生惰性,分析汇编能力会下降。
int __fastcall sub_80905D1C(int a1, int a2, int a3, int a4) { int v4; // r6@1 int v5; // r4@1 int v6; // r7@1 int v7; // r0@1 v4 = a4; v5 = a1; v6 = (*(int (**)(void))(*(_DWORD *)a1 + 676))(); v7 = (*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)v5 + 676))(v5, v4, 0); ((void (__fastcall *)(_UNKNOWN *, int, int))sub_809055F8)(&"Failure", v6, v7); ((void (__fastcall *)(_UNKNOWN *))sub_80905C44)(&"Failure"); return (*(int (__fastcall **)(int, _UNKNOWN *))(*(_DWORD *)v5 + 668))(v5, &"Failure"); }
int __fastcall sub_809055F8(int a1, int a2, int a3) { int v3; // r6@1 int v4; // r4@1 int v5; // r5@1 int result; // r0@1 int v7; // r0@3 int v8; // r7@3 int v9; // r0@3 int v10; // r7@3 int v11; // r3@3 int v12; // [sp+4h] [bp-24h]@3 int v13; // [sp+8h] [bp-20h]@3 int v14; // [sp+Ch] [bp-1Ch]@3 v3 = a3; v4 = a1; v5 = a2; result = ((int (*)(void))unk_809055B4)(); if ( v3 ) { if ( v5 ) { v7 = ((int (__fastcall *)(int))strlen_0)(v5); v13 = v7; v8 = v7; v9 = ((int (__fastcall *)(int))strlen_0)(v3); v10 = v8 + 1; v14 = v9; v12 = v9 + 1; *(_DWORD *)(v4 + 52) = ((int (__fastcall *)(int))malloc_0)(v10); result = ((int (__fastcall *)(int))malloc_0)(v12); v11 = *(_DWORD *)(v4 + 52); *(_DWORD *)(v4 + 56) = result; if ( v11 ) { if ( result ) { ((void (__fastcall *)(int, _DWORD, int))memset_0)(v11, 0, v10); ((void (__fastcall *)(_DWORD, _DWORD, int))memset_0)(*(_DWORD *)(v4 + 56), 0, v12); ((void (__fastcall *)(_DWORD, int, int))memcpy_0)(*(_DWORD *)(v4 + 52), v5, v13); result = ((int (__fastcall *)(_DWORD, int, int))memcpy_0)(*(_DWORD *)(v4 + 56), v3, v14); } } } } return result; }
int __fastcall sub_809055B4(int a1) { int v1; // r4@1 v1 = a1; if ( *(_DWORD *)(a1 + 52) ) { ((void (*)(void))free)(); *(_DWORD *)(v1 + 52) = 0; } if ( *(_DWORD *)(v1 + 56) ) { ((void (*)(void))free)(); *(_DWORD *)(v1 + 56) = 0; } memset_0(v1 + 60, 0, 30); return memset_0(v1, 0, 50); }
接下来的工作分析吧,很常规了。。。
附两个头可以直接导入,使用其中的结构体,但是对于C++方式结构体,即类的不知道怎么导入,记住先倒入依赖的头stddarg.h(jni依赖它)
/* * stdarg.h * * Provides facilities for stepping through a list of function arguments of * an unknown number and type. * * NOTE: Gcc should provide stdarg.h, and I believe their version will work * with crtdll. If necessary I think you can replace this with the GCC * stdarg.h. * * Note that the type used in va_arg is supposed to match the actual type * *after default promotions*. Thus, va_arg (..., short) is not valid. * * This file is part of the Mingw32 package. * * Contributors: * Created by Colin Peters <colin@bird.fu.is.saga-u.ac.jp> * * THIS SOFTWARE IS NOT COPYRIGHTED * * This source code is offered for use in the public domain. You may * use, modify or distribute it freely. * * This code is distributed in the hope that it will be useful but * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY * DISCLAMED. This includes but is not limited to warranties of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * $Revision: 1.2 $ * $Author: noer $ * $Date: 1998/10/10 00:51:16 $ * */ #ifndef _STDARG_H_ #define _STDARG_H_ /* * Don't do any of this stuff for the resource compiler. */ #ifndef RC_INVOKED /* * I was told that Win NT likes this. */ #ifndef _VA_LIST_DEFINED #define _VA_LIST_DEFINED #endif #ifndef _VA_LIST #define _VA_LIST typedef char* va_list; #endif /* * Amount of space required in an argument list (ie. the stack) for an * argument of type t. */ #define __va_argsiz(t) \ (((sizeof(t) + sizeof(int) - 1) / sizeof(int)) * sizeof(int)) /* * Start variable argument list processing by setting AP to point to the * argument after pN. */ #ifdef __GNUC__ /* * In GNU the stack is not necessarily arranged very neatly in order to * pack shorts and such into a smaller argument list. Fortunately a * neatly arranged version is available through the use of __builtin_next_arg. */ #define va_start(ap, pN) \ ((ap) = ((va_list) __builtin_next_arg(pN))) #else /* * For a simple minded compiler this should work (it works in GNU too for * vararg lists that don't follow shorts and such). */ #define va_start(ap, pN) \ ((ap) = ((va_list) (&pN) + __va_argsiz(pN))) #endif /* * End processing of variable argument list. In this case we do nothing. */ #define va_end(ap) ((void)0) /* * Increment ap to the next argument in the list while returing a * pointer to what ap pointed to first, which is of type t. * * We cast to void* and then to t* because this avoids a warning about * increasing the alignment requirement. */ #define va_arg(ap, t) \ (((ap) = (ap) + __va_argsiz(t)), \ *((t*) (void*) ((ap) - __va_argsiz(t)))) #endif /* Not RC_INVOKED */ #endif /* not _STDARG_H_ */ stdarg.h