Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
S
SandHook
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
SandHook
Commits
2e3b55f1
Commit
2e3b55f1
authored
May 24, 2019
by
swift_gan
Committed by
swift_gan
May 24, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[NativeHook]add elf & tweak cmake
parent
2f56fd50
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
268 additions
and
86 deletions
+268
-86
CMakeLists.txt
nativehook/CMakeLists.txt
+23
-17
code_relocate_arm64.cpp
...main/cpp/archs/arm/arm64/relocate/code_relocate_arm64.cpp
+1
-6
elf.cpp
nativehook/src/main/cpp/elf/elf.cpp
+146
-0
elf.h
nativehook/src/main/cpp/includes/elf.h
+60
-0
sandhook_native.cpp
nativehook/src/main/cpp/sandhook_native.cpp
+30
-63
sandhook_native.h
nativehook/src/main/cpp/sandhook_native.h
+8
-0
No files found.
nativehook/CMakeLists.txt
View file @
2e3b55f1
...
@@ -9,6 +9,28 @@ cmake_minimum_required(VERSION 3.4.1)
...
@@ -9,6 +9,28 @@ cmake_minimum_required(VERSION 3.4.1)
# or SHARED, and provides the relative paths to its source code.
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
# Gradle automatically packages shared libraries with your APK.
if
(
${
CMAKE_ANDROID_ARCH_ABI
}
STREQUAL
"arm64-v8a"
)
set
(
OS_DEPENDENDED_SRC
src/main/cpp/archs/arm/arm64/assembler/assembler_arm64.cpp
src/main/cpp/archs/arm/arm64/inst/inst_arm64.cpp
src/main/cpp/archs/arm/arm64/register/register_arm64.cpp
src/main/cpp/archs/arm/arm64/register/register_list_arm64.cpp
src/main/cpp/archs/arm/arm64/decoder/decoder_arm64.cpp
src/main/cpp/archs/arm/arm64/relocate/code_relocate_arm64.cpp
src/main/cpp/archs/arm/arm64/hook/hook_arm64.cpp
)
elseif
(
${
CMAKE_ANDROID_ARCH_ABI
}
STREQUAL
"armeabi-v7a"
)
set
(
OS_DEPENDENDED_SRC
src/main/cpp/archs/arm/arm32/inst/inst_arm32.cpp
src/main/cpp/archs/arm/arm32/inst/inst_t32.cpp
src/main/cpp/archs/arm/arm32/inst/inst_t16.cpp
src/main/cpp/archs/arm/arm32/register/register_arm32.cpp
src/main/cpp/archs/arm/arm32/register/register_list_arm32.cpp
src/main/cpp/archs/arm/arm32/assembler/assembler_arm32.cpp
src/main/cpp/archs/arm/arm32/decoder/decoder_arm32.cpp
src/main/cpp/archs/arm/arm32/hook/hook_arm32.cpp
src/main/cpp/archs/arm/arm32/relocate/code_relocate_arm32.cpp
)
endif
()
add_library
(
# Sets the name of the library.
add_library
(
# Sets the name of the library.
sandhook-native
sandhook-native
...
@@ -19,29 +41,13 @@ add_library( # Sets the name of the library.
...
@@ -19,29 +41,13 @@ add_library( # Sets the name of the library.
# Provides a relative path to your source file(s).
# Provides a relative path to your source file(s).
src/main/cpp/sandhook_native.cpp
src/main/cpp/sandhook_native.cpp
src/main/cpp/decoder/decoder.cpp
src/main/cpp/decoder/decoder.cpp
src/main/cpp/archs/arm/arm64/assembler/assembler_arm64.cpp
src/main/cpp/archs/arm/arm64/inst/inst_arm64.cpp
src/main/cpp/archs/arm/arm64/register/register_arm64.cpp
src/main/cpp/archs/arm/arm64/register/register_list_arm64.cpp
src/main/cpp/archs/arm/arm64/decoder/decoder_arm64.cpp
src/main/cpp/archs/arm/arm64/relocate/code_relocate_arm64.cpp
src/main/cpp/archs/arm/arm64/hook/hook_arm64.cpp
src/main/cpp/archs/arm/arm32/inst/inst_arm32.cpp
src/main/cpp/archs/arm/arm32/inst/inst_t32.cpp
src/main/cpp/archs/arm/arm32/inst/inst_t16.cpp
src/main/cpp/archs/arm/arm32/register/register_arm32.cpp
src/main/cpp/archs/arm/arm32/register/register_list_arm32.cpp
src/main/cpp/archs/arm/arm32/assembler/assembler_arm32.cpp
src/main/cpp/archs/arm/arm32/decoder/decoder_arm32.cpp
src/main/cpp/archs/arm/arm32/hook/hook_arm32.cpp
src/main/cpp/archs/arm/arm32/relocate/code_relocate_arm32.cpp
src/main/cpp/relocate/code_relocate.cpp
src/main/cpp/relocate/code_relocate.cpp
src/main/cpp/elf/elf.cpp
src/main/cpp/elf/elf.cpp
src/main/cpp/assembler/assembler.cpp
src/main/cpp/assembler/assembler.cpp
src/main/cpp/buffer/code_buffer.cpp
src/main/cpp/buffer/code_buffer.cpp
src/main/cpp/utils/platform.cpp
src/main/cpp/utils/platform.cpp
src/main/cpp/hook/hook.cpp
src/main/cpp/hook/hook.cpp
)
${
OS_DEPENDENDED_SRC
}
)
include_directories
(
include_directories
(
...
...
nativehook/src/main/cpp/archs/arm/arm64/relocate/code_relocate_arm64.cpp
View file @
2e3b55f1
...
@@ -85,7 +85,7 @@ IMPL_RELOCATE(B_BL) {
...
@@ -85,7 +85,7 @@ IMPL_RELOCATE(B_BL) {
Addr
targetAddr
=
inst
->
getImmPCOffsetTarget
();
Addr
targetAddr
=
inst
->
getImmPCOffsetTarget
();
if
(
inst
->
op
==
inst
->
BL
)
{
if
(
inst
->
op
==
inst
->
BL
)
{
__
Mov
(
LR
,
(
Addr
)
toPc
+
inst
->
size
());
__
Mov
(
LR
,
(
Addr
)
inst
->
getPC
()
+
inst
->
size
());
}
}
__
Mov
(
IP1
,
targetAddr
);
__
Mov
(
IP1
,
targetAddr
);
__
Br
(
IP1
);
__
Br
(
IP1
);
...
@@ -207,10 +207,5 @@ IMPL_RELOCATE(LDR_LIT) {
...
@@ -207,10 +207,5 @@ IMPL_RELOCATE(LDR_LIT) {
}
}
IMPL_RELOCATE
(
ADR_ADRP
)
{
IMPL_RELOCATE
(
ADR_ADRP
)
{
if
(
inRelocateRange
(
inst
->
offset
,
sizeof
(
Addr
)))
{
__
Mov
(
*
inst
->
rd
,
(
Addr
)
toPc
);
}
__
Mov
(
*
inst
->
rd
,
inst
->
getImmPCOffsetTarget
());
__
Mov
(
*
inst
->
rd
,
inst
->
getImmPCOffsetTarget
());
}
}
nativehook/src/main/cpp/elf/elf.cpp
View file @
2e3b55f1
...
@@ -3,3 +3,149 @@
...
@@ -3,3 +3,149 @@
//
//
#include "elf.h"
#include "elf.h"
#include <malloc.h>
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include "log.h"
using
namespace
SandHook
::
Elf
;
ElfImg
::
ElfImg
(
const
char
*
elf
)
{
this
->
elf
=
elf
;
//load elf
int
fd
=
open
(
elf
,
O_RDONLY
);
if
(
fd
<
0
)
{
LOGE
(
"failed to open %s"
,
elf
);
return
;
}
size
=
lseek
(
fd
,
0
,
SEEK_END
);
if
(
size
<=
0
)
{
LOGE
(
"lseek() failed for %s"
,
elf
);
}
header
=
reinterpret_cast
<
Elf_Ehdr
*>
(
mmap
(
0
,
size
,
PROT_READ
,
MAP_SHARED
,
fd
,
0
));
close
(
fd
);
Elf_Off
symtab_entsize
=
0
;
section_header
=
reinterpret_cast
<
Elf_Shdr
*>
(((
uint8_t
*
)
header
)
+
header
->
e_shoff
);
uint8_t
*
shoff
=
reinterpret_cast
<
uint8_t
*>
(
section_header
);
char
*
section_str
=
reinterpret_cast
<
char
*>
(
section_header
[
header
->
e_shstrndx
].
sh_offset
+
((
uint8_t
*
)
header
));
bool
has_strtab
=
false
;
bool
has_dynsym
=
false
;
for
(
int
i
=
0
;
i
<
header
->
e_shnum
;
i
++
,
shoff
+=
header
->
e_shentsize
)
{
Elf_Shdr
*
section_h
=
(
Elf_Shdr
*
)
shoff
;
char
*
sname
=
section_h
->
sh_name
+
section_str
;
switch
(
section_h
->
sh_type
)
{
case
SHT_DYNSYM
:
has_dynsym
=
true
;
dynsym
=
section_h
;
break
;
case
SHT_SYMTAB
:
if
(
strcmp
(
sname
,
".symtab"
)
==
0
)
{
symtab
=
section_h
;
symtab_offset
=
section_h
->
sh_offset
;
symtab_size
=
section_h
->
sh_size
;
symtab_entsize
=
section_h
->
sh_entsize
;
symtab_count
=
symtab_size
/
symtab_entsize
;
}
break
;
case
SHT_STRTAB
:
has_strtab
=
true
;
if
(
strcmp
(
sname
,
".strtab"
)
==
0
)
{
strtab
=
section_h
;
symstr_offset
=
section_h
->
sh_offset
;
}
break
;
case
SHT_PROGBITS
:
if
(
has_dynsym
&&
has_strtab
&&
bias
==
-
4396
)
{
bias
=
(
off_t
)
section_h
->
sh_addr
-
(
off_t
)
section_h
->
sh_offset
;
}
break
;
}
}
if
(
!
symtab_offset
)
{
LOGW
(
"can't find symtab from sections
\n
"
);
}
sym_start
=
reinterpret_cast
<
Elf_Sym
*>
(((
uint8_t
*
)
header
)
+
symtab_offset
);
//load module base
base
=
reinterpret_cast
<
uint8_t
*>
(
getModuleBase
(
elf
));
}
ElfImg
::~
ElfImg
()
{
//open elf file local
if
(
buffer
)
{
free
(
buffer
);
buffer
=
nullptr
;
}
//use mmap
if
(
header
)
{
munmap
(
header
,
size
);
}
}
Elf_Addr
ElfImg
::
getSymbOffset
(
const
char
*
name
)
{
Elf_Addr
_offset
=
0
;
for
(
int
i
=
0
;
i
<
symtab_count
;
i
++
)
{
unsigned
int
st_type
=
ELF_ST_TYPE
(
sym_start
[
i
].
st_info
);
char
*
st_name
=
reinterpret_cast
<
char
*>
(((
uint8_t
*
)
header
)
+
symstr_offset
+
sym_start
[
i
].
st_name
);
if
(
st_type
==
STT_FUNC
&&
sym_start
[
i
].
st_size
)
{
if
(
strcmp
(
st_name
,
name
)
==
0
)
{
_offset
=
sym_start
[
i
].
st_value
;
LOGD
(
"find %s: %x
\n
"
,
elf
,
_offset
);
break
;
}
}
}
return
_offset
;
}
Elf_Addr
ElfImg
::
getSymbAddress
(
const
char
*
name
)
{
Elf_Addr
offset
=
getSymbOffset
(
name
);
if
(
offset
>
0
&&
base
!=
nullptr
)
{
return
reinterpret_cast
<
Elf_Addr
>
(
base
+
offset
-
bias
);
}
else
{
return
0
;
}
}
void
*
ElfImg
::
getModuleBase
(
const
char
*
name
)
{
FILE
*
maps
;
char
buff
[
256
];
off_t
load_addr
;
int
found
=
0
;
maps
=
fopen
(
"/proc/self/maps"
,
"r"
);
while
(
fgets
(
buff
,
sizeof
(
buff
),
maps
))
{
if
((
strstr
(
buff
,
"r-xp"
)
||
strstr
(
buff
,
"r--p"
))
&&
strstr
(
buff
,
name
))
{
found
=
1
;
LOGD
(
"dlopen"
,
"%s
\n
"
,
buff
);
break
;
}
}
if
(
!
found
)
{
LOGE
(
"failed to read load address for %s"
,
name
);
return
nullptr
;
}
if
(
sscanf
(
buff
,
"%lx"
,
&
load_addr
)
!=
1
)
LOGE
(
"failed to read load address for %s"
,
name
);
fclose
(
maps
);
LOGD
(
"get module base %s: %lu"
,
name
,
load_addr
);
return
reinterpret_cast
<
void
*>
(
load_addr
);
}
nativehook/src/main/cpp/includes/elf.h
View file @
2e3b55f1
...
@@ -5,4 +5,64 @@
...
@@ -5,4 +5,64 @@
#ifndef SANDHOOK_NH_ELF_H
#ifndef SANDHOOK_NH_ELF_H
#define SANDHOOK_NH_ELF_H
#define SANDHOOK_NH_ELF_H
#include <linux/elf.h>
#include "base.h"
#if defined(__LP64__)
typedef
Elf64_Ehdr
Elf_Ehdr
;
typedef
Elf64_Shdr
Elf_Shdr
;
typedef
Elf64_Addr
Elf_Addr
;
typedef
Elf64_Dyn
Elf_Dyn
;
typedef
Elf64_Rela
Elf_Rela
;
typedef
Elf64_Sym
Elf_Sym
;
typedef
Elf64_Off
Elf_Off
;
#define ELF_R_SYM(i) ELF64_R_SYM(i)
#else
typedef
Elf32_Ehdr
Elf_Ehdr
;
typedef
Elf32_Shdr
Elf_Shdr
;
typedef
Elf32_Addr
Elf_Addr
;
typedef
Elf32_Dyn
Elf_Dyn
;
typedef
Elf32_Rel
Elf_Rela
;
typedef
Elf32_Sym
Elf_Sym
;
typedef
Elf32_Off
Elf_Off
;
#define ELF_R_SYM(i) ELF32_R_SYM(i)
#endif
namespace
SandHook
{
namespace
Elf
{
class
ElfImg
{
public
:
ElfImg
(
const
char
*
elf
);
Elf_Addr
getSymbOffset
(
const
char
*
name
);
void
*
getModuleBase
(
const
char
*
name
);
Elf_Addr
getSymbAddress
(
const
char
*
name
);
~
ElfImg
();
private
:
const
char
*
elf
=
nullptr
;
U8
*
base
=
nullptr
;
char
*
buffer
=
nullptr
;
off_t
size
=
0
;
off_t
bias
=
-
4396
;
Elf_Ehdr
*
header
=
nullptr
;
Elf_Shdr
*
section_header
=
nullptr
;
Elf_Shdr
*
symtab
=
nullptr
;
Elf_Shdr
*
strtab
=
nullptr
;
Elf_Shdr
*
dynsym
=
nullptr
;
Elf_Sym
*
sym_start
=
nullptr
;
Elf_Off
symtab_count
=
0
;
Elf_Off
symstr_offset
=
0
;
Elf_Off
symtab_offset
=
0
;
Elf_Off
symtab_size
=
0
;
};
}
}
#endif //SANDHOOK_NH_ELF_H
#endif //SANDHOOK_NH_ELF_H
nativehook/src/main/cpp/sandhook_native.cpp
View file @
2e3b55f1
...
@@ -4,17 +4,13 @@
...
@@ -4,17 +4,13 @@
#include <jni.h>
#include <jni.h>
#include <sys/mman.h>
#include <sys/mman.h>
#include <log.h>
#include "sandhook_native.h"
#include "sandhook_native.h"
#include "inst_arm64.h"
#include "decoder_arm64.h"
#include "hook.h"
#include "hook.h"
#include "
log
.h"
#include "
elf
.h"
using
namespace
SandHook
::
Asm
;
using
namespace
SandHook
::
Decoder
;
using
namespace
SandHook
::
Hook
;
using
namespace
SandHook
::
Hook
;
using
namespace
SandHook
::
AsmA64
;
using
namespace
SandHook
::
Elf
;
int
m1
=
5
;
int
m1
=
5
;
int
m3
=
1036
;
int
m3
=
1036
;
...
@@ -25,15 +21,12 @@ void (*dosth3Backup)(int, int) = nullptr;
...
@@ -25,15 +21,12 @@ void (*dosth3Backup)(int, int) = nullptr;
void
(
*
dosth4Backup
)()
=
nullptr
;
void
(
*
dosth4Backup
)()
=
nullptr
;
void
(
*
dosth4Backup2
)()
=
nullptr
;
void
(
*
dosth4Backup2
)()
=
nullptr
;
bool
memUnprotect
(
Addr
addr
,
Addr
len
)
{
void
(
*
innerSuspendVM
)()
=
nullptr
;
long
pagesize
=
4096
;
unsigned
alignment
=
(
unsigned
)((
unsigned
long
long
)
addr
%
pagesize
);
int
i
=
mprotect
((
void
*
)
(
addr
-
alignment
),
(
size_t
)
(
alignment
+
len
),
void
SuspendVMReplace
()
{
PROT_READ
|
PROT_WRITE
|
PROT_EXEC
);
LOGE
(
"VM Suspend!"
);
if
(
i
==
-
1
)
{
innerSuspendVM
();
return
false
;
}
return
true
;
}
}
void
do2
()
{
void
do2
()
{
...
@@ -57,10 +50,6 @@ void do5() {
...
@@ -57,10 +50,6 @@ void do5() {
}
}
void
do4
()
{
void
do4
()
{
if
(
m3
>
m2
)
{
return
;
}
do5
();
int
a
=
1
+
1
;
int
a
=
1
+
1
;
int
b
=
a
+
1
;
int
b
=
a
+
1
;
int
d
=
a
+
1
;
int
d
=
a
+
1
;
...
@@ -78,7 +67,6 @@ void do4replace() {
...
@@ -78,7 +67,6 @@ void do4replace() {
void
do4replace2
()
{
void
do4replace2
()
{
int
a
=
1
+
1
;
int
a
=
1
+
1
;
int
b
=
1
+
1
;
int
c
=
1
+
1
;
int
c
=
1
+
1
;
int
d
=
1
+
1
;
int
d
=
1
+
1
;
dosth4Backup2
();
dosth4Backup2
();
...
@@ -96,6 +84,7 @@ void do1() {
...
@@ -96,6 +84,7 @@ void do1() {
do2
();
do2
();
}
}
class
Visitor
:
public
InstVisitor
{
class
Visitor
:
public
InstVisitor
{
bool
visit
(
Unit
<
Base
>
*
unit
,
void
*
pc
)
override
{
bool
visit
(
Unit
<
Base
>
*
unit
,
void
*
pc
)
override
{
Instruction
<
Base
>*
instruction
=
reinterpret_cast
<
Instruction
<
Base
>
*>
(
unit
);
Instruction
<
Base
>*
instruction
=
reinterpret_cast
<
Instruction
<
Base
>
*>
(
unit
);
...
@@ -108,44 +97,6 @@ extern "C"
...
@@ -108,44 +97,6 @@ extern "C"
JNIEXPORT
void
JNICALL
JNIEXPORT
void
JNICALL
Java_com_swift_sandhook_nativehook_NativeHook_test
(
JNIEnv
*
env
,
jclass
jclass1
)
{
Java_com_swift_sandhook_nativehook_NativeHook_test
(
JNIEnv
*
env
,
jclass
jclass1
)
{
// union {
// InstA64 raw = 0xF9001043;
// STRUCT_A64(STR_UIMM) str;
// } test;
//
// InstA64* codebl = reinterpret_cast<InstA64 *>((Addr)do1 + 8);
//
// if (IS_OPCODE_A64(*codebl, B_BL)) {
//
// //decode
// A64_B_BL a64bl(*reinterpret_cast<STRUCT_A64(B_BL)*>(codebl));
// Off off = a64bl.offset;
// void (*dosth2)() =reinterpret_cast<void (*)()>(a64bl.getImmPCOffsetTarget());
// dosth2();
//
// //asm
// memUnprotect(reinterpret_cast<Addr>(a64bl.get()), a64bl.size());
// a64bl.assembler();
// Off off1 = a64bl.getImmPCOffset();
//
// do1();
//
// }
//
// if (IS_OPCODE_A64(test.raw, STR_UIMM)) {
// A64_STR_UIMM str(test.str);
// str.assembler();
// str.get();
// }
//
// Arm64Decoder arm64Decoder = Arm64Decoder();
//
// Visitor visitor = Visitor();
//
// arm64Decoder.decode(reinterpret_cast<void *>(do1), 4 * 8, visitor);
//
// InlineHookArm64Android inlineHookArm64Android = InlineHookArm64Android();
//
void
*
do5addr
=
reinterpret_cast
<
void
*>
(
do5
);
void
*
do5addr
=
reinterpret_cast
<
void
*>
(
do5
);
void
*
do4addr
=
reinterpret_cast
<
void
*>
(
do4
);
void
*
do4addr
=
reinterpret_cast
<
void
*>
(
do4
);
...
@@ -154,11 +105,27 @@ Java_com_swift_sandhook_nativehook_NativeHook_test(JNIEnv *env, jclass jclass1)
...
@@ -154,11 +105,27 @@ Java_com_swift_sandhook_nativehook_NativeHook_test(JNIEnv *env, jclass jclass1)
reinterpret_cast
<
void
*>
(
do4
),
reinterpret_cast
<
void
*>
(
do4
),
reinterpret_cast
<
void
*>
(
do4replace
)));
reinterpret_cast
<
void
*>
(
do4replace
)));
dosth4Backup2
=
reinterpret_cast
<
void
(
*
)()
>
(
InlineHook
::
instance
->
inlineHook
(
reinterpret_cast
<
void
*>
(
do4
),
reinterpret_cast
<
void
*>
(
do4replace2
)));
do4
();
do4
();
//innerSuspendVM = reinterpret_cast<void (*)()>(SandInlineHookSym("/system/lib/libart.so", "_ZN3art3Dbg9SuspendVMEv",
// reinterpret_cast<void *>(SuspendVMReplace)));
}
extern
"C"
EXPORT
void
*
SandInlineHook
(
void
*
origin
,
void
*
replace
)
{
return
InlineHook
::
instance
->
inlineHook
(
origin
,
replace
);
}
extern
"C"
EXPORT
void
*
SandInlineHookSym
(
const
char
*
so
,
const
char
*
symb
,
void
*
replace
)
{
ElfImg
elfImg
(
so
);
void
*
origin
=
reinterpret_cast
<
void
*>
(
elfImg
.
getSymbAddress
(
symb
));
if
(
origin
==
nullptr
)
return
nullptr
;
return
InlineHook
::
instance
->
inlineHook
(
origin
,
replace
);
}
}
\ No newline at end of file
nativehook/src/main/cpp/sandhook_native.h
View file @
2e3b55f1
...
@@ -5,4 +5,12 @@
...
@@ -5,4 +5,12 @@
#ifndef SANDHOOK_SANDHOOK_NATIVE_H
#ifndef SANDHOOK_SANDHOOK_NATIVE_H
#define SANDHOOK_SANDHOOK_NATIVE_H
#define SANDHOOK_SANDHOOK_NATIVE_H
#define EXPORT __attribute__ ((visibility ("default")))
extern
"C"
EXPORT
void
*
SandInlineHook
(
void
*
origin
,
void
*
replace
);
extern
"C"
EXPORT
void
*
SandInlineHookSym
(
const
char
*
so
,
const
char
*
symb
,
void
*
replace
);
#endif //SANDHOOK_SANDHOOK_NATIVE_H
#endif //SANDHOOK_SANDHOOK_NATIVE_H
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment