Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate JNI.h types into the decompiled code #49

Closed
enovella opened this issue Oct 23, 2019 · 10 comments · Fixed by #246
Closed

Integrate JNI.h types into the decompiled code #49

enovella opened this issue Oct 23, 2019 · 10 comments · Fixed by #246
Assignees
Labels
Projects

Comments

@enovella
Copy link

enovella commented Oct 23, 2019

Ghidra and IDA produce a clear and beautiful decompiled native code using the JNI types. Further info at https://www.ayrx.me/ghidra-jnianalyzer

ghidra

Radare2 has built-in the jni.h headers:

[0x003622bc]> pfo
elf64
jni.h
elf32
dex.h
pe32
elf_enums
meson.build
fat
zip
bios
dll
mz
dex
macho
cdex.h
ntfs
trx
[0x003622bc]> pfo jni.h

Thus, we can set the JNIEnv* for the first argument of the function Java_xxxx:

[0x003622bc]> pd 9352: sym.Java_com_zimperium_zdetection_service_ZVpnService_sendFDbyJNI (int32_t arg5, int32_t arg4, int32_t arg3, int32_t arg2, int32_t arg1);
│ bp: 1 (vars 1, args 0)
│ sp: 0 (vars 0, args 0)
│ rg: 5 (vars 0, args 5)
│           0x003622bc      fc0f1af8       str x28, [sp, -0x60]!
│           0x003622c0      fa6701a9       stp x26, x25, [sp, 0x10]
│           0x003622c4      f85f02a9       stp x24, x23, [sp, 0x20]
│           0x003622c8      f65703a9       stp x22, x21, [sp, 0x30]
│           0x003622cc      f44f04a9       stp x20, x19, [sp, 0x40]
│           0x003622d0      fd7b05a9       stp x29, x30, [sp, 0x50]
│           0x003622d4      fd430191       add x29, sp, 0x500x003622d8      ff8313d1       sub sp, sp, 0x4e00x003622dc      f40300aa       mov x20, x0                 ; arg1
[0x003622bc]> afvt arg5 JNIEnv*
[0x003622bc]> pd 9352: sym.Java_com_zimperium_zdetection_service_ZVpnService_sendFDbyJNI (JNIEnv*arg5, int32_t arg4, int32_t arg3, int32_t arg2, int32_t arg1);
│ bp: 1 (vars 1, args 0)
│ sp: 0 (vars 0, args 0)
│ rg: 5 (vars 0, args 5)
│           0x003622bc      fc0f1af8       str x28, [sp, -0x60]!
│           0x003622c0      fa6701a9       stp x26, x25, [sp, 0x10]
│           0x003622c4      f85f02a9       stp x24, x23, [sp, 0x20]
│           0x003622c8      f65703a9       stp x22, x21, [sp, 0x30]
│           0x003622cc      f44f04a9       stp x20, x19, [sp, 0x40]
│           0x003622d0      fd7b05a9       stp x29, x30, [sp, 0x50]
│           0x003622d4      fd430191       add x29, sp, 0x500x003622d8      ff8313d1       sub sp, sp, 0x4e00x003622dc      f40300aa       mov x20, x0                 ; arg1

However, r2ghidra gives us some warnings about the JNIEnv types:

[0x003622bc]> pdg

// WARNING: Unknown calling convention yet parameter storage is locked
// WARNING: [r2ghidra] Failed to match radare2 calling convention arm64 to Decompiler ProtoModel
// WARNING: [r2ghidra] Failed to match type JNIEnv* for variable arg5 to Decompiler type: Unknown type identifier JNIEnv
// WARNING: [r2ghidra] Failed to find return address in ProtoModel

void sym.Java_com_zimperium_zdetection_service_ZVpnService_sendFDbyJNI
               (int32_t arg1, int32_t arg4, int32_t arg3, int32_t arg2, undefined8 arg5)
{
...    
    piVar3 = (int64_t *)(uint64_t)(uint32_t)arg1;
    uVar4 = (**(code **)(*piVar3 + 0x548))(piVar3, (uint64_t)(uint32_t)arg4, 0);
    uVar5 = (**(code **)(*piVar3 + 0x548))(piVar3, arg5, 0);

IDA and Ghidra perform it properly:
Screenshot from 2019-10-23 14-55-19

Sample:
libzcloud.zip

Related issue radareorg/radare2#15337 by @radare

@enovella
Copy link
Author

Sample:
libzcloud.zip

@enovella
Copy link
Author

Any clue how to tackle this issue?

@radare
Copy link
Contributor

radare commented Mar 17, 2020

there's a test for r2, maybe that works with r2ghidra, didnt tried

IMAGE 2020-03-17 11:48:35

@radare
Copy link
Contributor

radare commented Mar 17, 2020

works in r2, but not in r2ghidra-dec, @thestr4ng3r can you take a look?

[0x00000f60]> pd 20
╭ 500: sym.Java_com_app_ndh_NDHActivity_print (int16_t arg1, int16_t arg2, int16_t arg3);
│ bp: 0 (vars 0, args 0)
│ sp: 15 (vars 15, args 0)
│ rg: 3 (vars 0, args 3)
│           0x00000f60      30b5               push {r4, r5, lr}
│           0x00000f62      cdb0               sub sp, 0x134
│           0x00000f64      7e4c               ldr r4, [0x00001160]                  ; [0x1160:4]=0x2b66
│           0x00000f66      7c44               add r4, pc
│           0x00000f68      0390               str r0, [sp + var_128h]               ; arg1
│           0x00000f6a      0291               str r1, [sp + var_12ch]               ; arg2
│           0x00000f6c      0192               str r2, [sp + var_130h]               ; arg3
│           0x00000f6e      7d4b               ldr r3, [0x00001164]                  ; [0x1164:4]=56
│           0x00000f70      e358               ldr r3, [r4, r3]
│           0x00000f72      1b68               ldr r3, [r3]
│           0x00000f74      4b93               str r3, [sp + JNINativeInterface]
│           0x00000f76      039b               ldr r3, [sp + var_128h]               ; JNINativeInterface.reserved0
│           0x00000f78      1a68               ldr r2, [r3]
│           0x00000f7a      a923               movs r3, 0xa9
│           0x00000f7c      9b00               lsls r3, r3, 2
│           0x00000f7e      d358               ldr r3, [r2, r3]                      ; JNINativeInterface.GetStringUTFChars
│           0x00000f80      0399               ldr r1, [sp + var_128h]
│           0x00000f82      019a               ldr r2, [sp + var_130h]               ; JNINativeInterface.reserved0
│           0x00000f84      081c               adds r0, r1, 0
│           0x00000f86      111c               adds r1, r2, 0
[0x00000f60]> pdg

// WARNING: [r2ghidra] Failed to match type struct for variable JNINativeInterface to Decompiler type: <string>:1:7:
// error: expected one of 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_' at end of input
//
// WARNING: [r2ghidra] Var arg_0h is stack pointer based, which is not supported for decompilation.

undefined4 sym.Java_com_app_ndh_NDHActivity_print(int16_t arg1, int16_t arg2, int16_t arg3)
{
    int32_t *piVar1;
    int32_t iVar2;
    undefined4 uVar3;
    uint32_t uVar4;
    int32_t iVar5;
    undefined4 uVar6;
    uint32_t uVar7;
    undefined8 uVar8;
    uint32_t auStack252 [4];
    undefined4 uStack236;
    undefined4 uStack232;
    undefined4 uStack228;
    undefined4 uStack224;
    undefined4 uStack220;
    undefined4 uStack216;
    undefined4 uStack212;
    undefined4 uStack208;
    uint32_t auStack204 [4];
    undefined4 uStack188;
    undefined4 uStack184;
    undefined4 uStack180;
    undefined4 uStack176;
    undefined4 uStack172;
    undefined4 uStack168;
    undefined4 uStack164;
    undefined4 uStack160;
    uint32_t auStack156 [4];
    undefined4 uStack140;
    undefined4 uStack136;
    undefined4 uStack132;
    undefined4 uStack128;
    undefined4 uStack124;
    undefined4 uStack120;
    undefined4 uStack116;
    undefined4 uStack112;
    undefined4 uStack108;
    undefined4 uStack104;
    undefined4 uStack100;
    undefined4 uStack96;
    undefined4 uStack92;
    undefined4 uStack88;
    undefined4 uStack84;
    undefined4 uStack80;
    undefined4 uStack76;
    undefined4 uStack72;
    undefined4 uStack68;
    undefined4 uStack64;
    int32_t iStack60;
    int32_t iStack56;
    int32_t iStack52;
    undefined4 uStack48;
    undefined4 uStack44;
    uint8_t *puStack40;
    uint8_t uStack33;
    int32_t iStack20;
    uint32_t uStack16;

    piVar1 = (int32_t *)(int32_t)arg1;
    iVar5 = *(int32_t *)0x1160 + 0xf6a;
    iStack20 = **(int32_t **)(iVar5 + *(int32_t *)0x1164);
    puStack40 = (uint8_t *)(**(code **)(*piVar1 + 0x2a4))(piVar1, (int32_t)arg3, 0);
    uStack44 = sym.imp.time(0);
    uStack108 = *(undefined4 *)(*(int32_t *)0x1168 + 0xfa2);
    uStack104 = *(undefined4 *)(*(int32_t *)0x1168 + 0xfa6);
    uStack100 = *(undefined4 *)(*(int32_t *)0x1168 + 0xfaa);
    uStack96 = *(undefined4 *)(*(int32_t *)0x1168 + 0xfae);
    uStack92 = *(undefined4 *)(*(int32_t *)0x1168 + 0xfb2);
    uStack88 = *(undefined4 *)(*(int32_t *)0x1168 + 0xfb6);
    uStack84 = *(undefined4 *)(*(int32_t *)0x1168 + 0xfba);
    uStack80 = *(undefined4 *)(*(int32_t *)0x1168 + 0xfbe);
    uStack76 = *(undefined4 *)(*(int32_t *)0x1168 + 0xfc2);
    uStack72 = *(undefined4 *)(*(int32_t *)0x1168 + 0xfc6);
    uStack68 = *(undefined4 *)(*(int32_t *)0x1168 + 0xfca);
    uStack64 = *(undefined4 *)(*(int32_t *)0x1168 + 0xfce);
    auStack156[0] = *(uint32_t *)(*(int32_t *)0x116c + 0xfb8);
    auStack156[1] = *(undefined4 *)(*(int32_t *)0x116c + 0xfbc);
    auStack156[2] = *(undefined4 *)(*(int32_t *)0x116c + 0xfc0);
    auStack156[3] = *(undefined4 *)(*(int32_t *)0x116c + 0xfc4);
    uStack140 = *(undefined4 *)(*(int32_t *)0x116c + 0xfc8);
    uStack136 = *(undefined4 *)(*(int32_t *)0x116c + 0xfcc);
    uStack132 = *(undefined4 *)(*(int32_t *)0x116c + 0xfd0);
    uStack128 = *(undefined4 *)(*(int32_t *)0x116c + 0xfd4);
    uStack124 = *(undefined4 *)(*(int32_t *)0x116c + 0xfd8);
    uStack120 = *(undefined4 *)(*(int32_t *)0x116c + 0xfdc);
    uStack116 = *(undefined4 *)(*(int32_t *)0x116c + 0xfe0);
    uStack112 = *(undefined4 *)(*(int32_t *)0x116c + 0xfe4);
    auStack204[0] = *(uint32_t *)(*(int32_t *)0x1170 + 0xfce);
    auStack204[1] = *(undefined4 *)(*(int32_t *)0x1170 + 0xfd2);
    auStack204[2] = *(undefined4 *)(*(int32_t *)0x1170 + 0xfd6);
    auStack204[3] = *(undefined4 *)(*(int32_t *)0x1170 + 0xfda);
    uStack188 = *(undefined4 *)(*(int32_t *)0x1170 + 0xfde);
    uStack184 = *(undefined4 *)(*(int32_t *)0x1170 + 0xfe2);
    uStack180 = *(undefined4 *)(*(int32_t *)0x1170 + 0xfe6);
    uStack176 = *(undefined4 *)(*(int32_t *)0x1170 + 0xfea);
    uStack172 = *(undefined4 *)(*(int32_t *)0x1170 + 0xfee);
    uStack168 = *(undefined4 *)(*(int32_t *)0x1170 + 0xff2);
    uStack164 = *(undefined4 *)(*(int32_t *)0x1170 + 0xff6);
    uStack160 = *(undefined4 *)(*(int32_t *)0x1170 + 0xffa);
    auStack252[0] = *(uint32_t *)(*(int32_t *)0x1174 + 0xfe4);
    auStack252[1] = *(undefined4 *)(*(int32_t *)0x1174 + 0xfe8);
    auStack252[2] = *(undefined4 *)(*(int32_t *)0x1174 + 0xfec);
    auStack252[3] = *(undefined4 *)(*(int32_t *)0x1174 + 0xff0);
    uStack236 = *(undefined4 *)(*(int32_t *)0x1174 + 0xff4);
    uStack232 = *(undefined4 *)(*(int32_t *)0x1174 + 0xff8);
    uStack228 = *(undefined4 *)(*(int32_t *)0x1174 + 0xffc);
    uStack224 = *(undefined4 *)(*(int32_t *)0x1174 + 0x1000);
    uStack220 = *(undefined4 *)(*(int32_t *)0x1174 + 0x1004);
    uStack216 = *(undefined4 *)(*(int32_t *)0x1174 + 0x1008);
    uStack212 = *(undefined4 *)(*(int32_t *)0x1174 + 0x100c);
    uStack208 = *(undefined4 *)(*(int32_t *)0x1174 + 0x1010);
    uStack33 = *puStack40;
    iStack52 = 0;
    iStack56 = 0;
    iStack60 = (**(code **)(*piVar1 + 0x290))(piVar1, (int32_t)arg3);
    uVar3 = *(undefined4 *)(*(int32_t *)0x117c + 0x102e);
    uVar6 = *(undefined4 *)(*(int32_t *)0x117c + 0x1032);
    *(undefined4 *)((int32_t)&uStack16 + *(int32_t *)0x1178) = *(undefined4 *)(*(int32_t *)0x117c + 0x102a);
    *(undefined4 *)(&stack0xfffffff4 + *(int32_t *)0x1178) = uVar3;
    *(undefined4 *)(&stack0xfffffff8 + *(int32_t *)0x1178) = uVar6;
    uVar3 = *(undefined4 *)(*(int32_t *)0x117c + 0x103a);
    uVar6 = *(undefined4 *)(*(int32_t *)0x117c + 0x103e);
    *(undefined4 *)(&stack0xfffffffc + *(int32_t *)0x1178) = *(undefined4 *)(*(int32_t *)0x117c + 0x1036);
    *(undefined4 *)(&stack0x00000000 + *(int32_t *)0x1178) = uVar3;
    *(undefined4 *)(&stack0x00000004 + *(int32_t *)0x1178) = uVar6;
    uVar3 = *(undefined4 *)(*(int32_t *)0x117c + 0x1046);
    uVar6 = *(undefined4 *)(*(int32_t *)0x117c + 0x104a);
    *(undefined4 *)(&stack0x00000008 + *(int32_t *)0x1178) = *(undefined4 *)(*(int32_t *)0x117c + 0x1042);
    *(undefined4 *)(&stack0x0000000c + *(int32_t *)0x1178) = uVar3;
    *(undefined4 *)(&stack0x00000010 + *(int32_t *)0x1178) = uVar6;
    uVar4 = *(uint32_t *)(*(int32_t *)0x117c + 0x1052);
    uVar3 = *(undefined4 *)(*(int32_t *)0x117c + 0x1056);
    uVar7 = *(int32_t *)0x117c + 0x105a;
    *(undefined4 *)(&stack0x00000014 + *(int32_t *)0x1178) = *(undefined4 *)(*(int32_t *)0x117c + 0x104e);
    *(uint32_t *)(&stack0x00000018 + *(int32_t *)0x1178) = uVar4;
    *(undefined4 *)(&stack0x0000001c + *(int32_t *)0x1178) = uVar3;
    while (uStack33 != 0) {
        if ((0xc < iStack52) || ((auStack156[iStack52] ^ auStack204[iStack52]) != (uint32_t)puStack40[iStack52])) {
            uVar3 = (**(code **)(*piVar1 + 0x29c))(piVar1, *(int32_t *)0x1180 + 0x1070);
            goto code_r0x0000113e;
        }
        uVar4 = *(uint32_t *)((int32_t)&uStack16 + iStack52 * 4 + *(int32_t *)0x1178) ^ auStack252[iStack52];
        *(uint32_t *)((int32_t)&uStack16 + iStack52 * 4 + *(int32_t *)0x1178) = uVar4;
        iStack52 = iStack52 + 1;
        iStack56 = iStack52 * 2;
        uVar7 = (uint32_t)puStack40[iStack52];
        uStack33 = puStack40[iStack52];
    }
    uStack48 = sym.imp.time(0, uVar4, uVar7);
    sym.imp.sleep(1);
    if (iStack60 == 0xc) {
        uVar8 = sym.imp.difftime(uStack44, uStack48);
        iVar2 = sym.__aeabi_dcmpgt((int32_t)((uint64_t)uVar8 >> 0x20), (int32_t)uVar8, *(undefined4 *)0x1158,
                                   *(undefined4 *)0x115c);
        if (iVar2 == 0) {
            uVar3 = (**(code **)(*piVar1 + 0x29c))(piVar1, *(int32_t *)0x1188 + 0x1138);
            goto code_r0x0000113e;
        }
    }
    uVar3 = (**(code **)(*piVar1 + 0x29c))(piVar1, *(int32_t *)0x1184 + 0x111e);
code_r0x0000113e:
    if (iStack20 != **(int32_t **)(iVar5 + *(int32_t *)0x1164)) {
        uVar3 = sym.imp.__stack_chk_fail();
    }
    return uVar3;
}
[0x00000f60]>

@thestr4ng3r thestr4ng3r self-assigned this Mar 17, 2020
@radare
Copy link
Contributor

radare commented Mar 17, 2020

@radare
Copy link
Contributor

radare commented Mar 18, 2020

@thestr4ng3r will be good to have tests for this. it is fixed?

@thestr4ng3r
Copy link
Member

Not entirely yet, it's still missing support for function pointers in structs on r2 side.

@XVilka XVilka added rizin Related to the Rizin plugin test-required types labels Jan 21, 2021
@XVilka XVilka added this to To do in Types via automation Jul 29, 2021
Types automation moved this from To do to Done Sep 14, 2021
@thestr4ng3r
Copy link
Member

With a slightly modified jni.h, this works now:
Bildschirmfoto 2021-09-14 um 18 01 08

@razaina
Copy link

razaina commented Oct 14, 2021

Hi @thestr4ng3r
what modification did you apply?

I am using radare2 5.4.2 and it looks like I don't even have jni.h.

[0x00000f54]> pfo jni.h
Parse error: tcc: error: file '/usr/share/radare2/5.4.2/format/jni.h' not found

@thestr4ng3r
Copy link
Member

rz-ghidra does not support radare2.
You can use rizin (https://github.com/rizinorg/rizin) with for example https://github.com/rizinorg/rz-ghidra/blob/dev/test/bins/jni-simplified.h

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Types
Done
Development

Successfully merging a pull request may close this issue.

5 participants