케이스1 (ret) 케이스2 (br) 케이스3 (b) 케이스4 (b) 케이스5 (br) 케이스6 (ret)
IDA 7.7 분석 안됨 분석 안됨 분석 안됨 정상 분석 됨 정상 분석 됨 분석 꼬임
Binary Ninja 3.3 분석 안됨 정상 분석 됨 정상 분석 됨 정상 분석 됨 정상 분석 됨 분석 꼬임

 

케이스 1 (ret)

int main(int argc, char const *argv[])
{
    printf("hello world!\n");
    __asm__ volatile(
        "adr x30,0x0 \n\t"
        "add x30,x30,0xc \n\t"
        "ret \n\t"
        );
    printf("test 1\n");
    printf("test 2\n");

    return 0;
}

IDA 7.7 분석 결과

 
방해 asm 이후 함수가 끝난 것으로 표시됨

분석이 asm 이후 되지 않음
분석이 asm 이후 되지 않음

Binary Ninja 3.3.3996 분석 결과

방해 asm 이후 함수가 끝난 것으로 표시됨
분석이 asm 이후 되지 않음

 

케이스 2 (br)

int main(int argc, char const *argv[])
{
    printf("hello world!\n");
    __asm__ volatile(
        "adr x30,0x0 \n\t"
        "add x30,x30,0xc \n\t"
        "br x30 \n\t"
        );
    printf("test 1\n");
    printf("test 2\n");

    return 0;
}

IDA 7.7 분석 결과

분석이 asm 이후 되지 않음

Binary Ninja 3.3.3996 분석 결과

정상적으로 분석 됨

케이스 3 (b)

#include <ctime>
int main(int argc, char const *argv[])
{
    printf("hello world!\n");
   
    // 항상 false가 되어야 실행되지 않는다.
    // 최적화 되지 않기 위해 불투명 술어로 코딩한다.
    if (rand() < 0) {
        __asm__(
            "b 0x4\n"
            ".long 12345678\n"
        );
    }
    printf("test 1\n");
    printf("test 2\n");

    return 0;
}

IDA 7.7 분석 결과

함수 전체가 분석 안됨

Binary Ninja 3.3.3996 분석 결과

정상적으로 분석 됨

 

케이스 4 (b)

#include <ctime>
int main(int argc, char const *argv[])
{
    printf("hello world!\n");
   
    // 항상 false가 되어야 실행되지 않는다.
    // 최적화 되지 않기 위해 불투명 술어로 코딩한다.
    if (rand() < 0) {
        __asm__(
            "b 0x4\n"
            "add sp,sp,#0x100\n"
            "add sp,sp,#0x100\n"
        );
    }
    printf("test 1\n");
    printf("test 2\n");

    return 0;
}

IDA 7.7 분석 결과

정상적으로 분석 됨

Binary Ninja 3.3.3996 분석 결과

정상적으로 분석 됨

케이스 5 (br)

int main(int argc, char const *argv[])
{
    printf("hello world!\n");

    if (rand() < 0) {
        __asm__(
            "mov x8,#0x1\n"
            "adr x9, #0x10\n"
            "mul x8, x9, x8\n"
            ".long 0x12345678\n"
            "br x8\n"
        );
    }
    printf("test 1\n");
    printf("test 2\n");

    return 0;
}

IDA 7.7 분석 결과

정상적으로 분석 됨

Binary Ninja 3.3.3996 분석 결과

정상적으로 분석 됨

케이스 6 (ret)

int main(int argc, char const *argv[])
{
    printf("hello world!\n");

    if (rand() < 0) {
        __asm__(
            "adr x8,#0xc\n"
            "mov x30,x8\n"
            "ret\n"
        );
    }
    printf("test 1\n");
    printf("test 2\n");

    return 0;
}

IDA 7.7 분석 결과

분석 꼬임

Binary Ninja 3.3.3996 분석 결과

분석 꼬임

 

 

https://android.googlesource.com/toolchain/llvm_android/+/master/README.md 참조하였음.

How to build in linux (or WSL for windows) 

윈도우를 위한 빌드 환경이 없으므로 wsl을 설치한다.

1. install curl & repo & python3

$ sudo apt install python3
$ sudo apt install python-is-python3
$ sudo apt install curl
$ curl https://storage.googleapis.com/git-repo-downloads/repo > repo
$ chmod 755 repo
$ cp repo ~

2. download and extract android-ndk-r25b-linux.zip

3. repo and build it

(If you want to build for windows, --no-build=linux is required.)

$ repo init -u https://android.googlesource.com/platform/manifest -b llvm-toolchain
$ cp android-ndk-r25b/toolchains/llvm/prebuilt/linux-x86_64/manifest_8490178.xml .repo/manifests
$ repo init -m manifest_8490178.xml
$ repo sync -c
$ python toolchain/llvm_android/build.py --no-build=linux

 

Apply to Visual Studio

1. cd C:\Microsoft\AndroidNDK

2. rename the directory on android-ndk-rXXX to android-ndk-rXXX_org

3. download & extract android-ndk-r25b-windows.zip and rename the directory android-ndk-r25b to android-ndk-rXXX

일반적으로 release 앱 권한에서는 fork & open /proc/child_pid/maps 시 권한이 없어서 "No such file or directory" 에러가 발생한다.

이를 해결하기 위해 아래와 같이 코딩한다.

prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); // enable
.. fork() 수행
.. open("/proc/child_pid/maps", ) 수행
.. ptrace() 수행
prctl(PR_SET_DUMPABLE, 0, 0, 0, 0); // disable

https://android.googlesource.com/platform/frameworks/base/+/master/core/jni/com_android_internal_os_Zygote.cpp 의 EnableDebugger()를 참조하였음

또한,  EnableDebugger()을 앱에 인젝션 하면 앱 디버깅 시 manifest를 수정하지 않아도 될 것 같다. (참고 : https://codetronik.tistory.com/165?category=1096665) 

 

(안드로이드) IDA 동적 디버깅

1. 리패키징 혹은 개발 과정에서 아래 코드를 추가 // build.gradle에 추가 buildTypes { release { debuggable true } // AndroidManifest.xml에 추가 2. adb forward tcp:23946 tcp:23946 3. adb push c:\ida\db..

codetronik.tistory.com

 

1. 리패키징 혹은 개발 과정에서 아래 코드를 추가

// build.gradle에 추가
buildTypes {
	release {
		debuggable true
	}
    
// AndroidManifest.xml에 추가
<application
	android:debuggable="true"     
	tools:ignore="HardcodedDebugMode">

2. adb forward tcp:23946 tcp:23946

3. adb push c:\ida\dbgsrv\android_server64 /data/local/tmp

4. 안드로이드에서 /android_server64 &

5. IDA->Debugger->Attach->Remote ARM Linux/Android debugger

6. Hostname 127.0.0.1 Port 23946

7. Run!

8. 앱 선택 후 attach 되면 F9

9. got SIGCHLD signal 창이 뜨면 F9클릭 후 Yes 클릭

 

 

분석 환경 : Galaxy Tab S6 Lite / Kernel 4.14.272 (https://github.com/LineageOS/android_kernel_samsung_gta4xl  Lineage OS 18.1 branch)

/drivers/soc/samsung/exynos-el3_mon.c

삼성 단말기에만 존재하는 커널 코드 보호 기능.

defconfig에서 CONFIG_EXYNOS_KERNEL_PROTECTION=n 으로 설정하면 빌드에 포함되지 않는다. 

실제로 코드 섹션이 변조 되는지는 확인 안해봤음

#ifdef CONFIG_EXYNOS_KERNEL_PROTECTION
static int __init exynos_protect_kernel_text(void)
{
	int ret = 0;
	unsigned long ktext_start_va = 0;
	unsigned long ktext_start_pa = 0;
	unsigned long ktext_end_va = 0;
	unsigned long ktext_end_pa = 0;

	/* Get virtual addresses of kernel text */
	ktext_start_va = (unsigned long)_text;	
	ktext_end_va = (unsigned long)_etext;

	/* Translate VA to PA */
	ktext_start_pa = (unsigned long)__pa_symbol(_text);
	ktext_end_pa = (unsigned long)__pa_symbol(_etext);

	/* Request to protect kernel text area */
	ret = exynos_smc(SMC_CMD_PROTECT_KERNEL_TEXT,
			ktext_start_pa,
			ktext_end_pa,
			0);
            
	return ret;	

	pr_info("%s: Success to set Kernel code as read-only\n", __func__);

	return 0;
}
core_initcall(exynos_protect_kernel_text);
#endif

 

/init/main.c

커널 init 시 실행되며, rodata 메모리 영역 보호 기능인 mark_rodata_ro()를 호출한다. 

rodata_enabled 변수는 mmu.c / module.c에서 사용하고 있으므로 false로 수정하면 변조하는데 있어서 좀 더 편해진다.

#if defined(CONFIG_STRICT_KERNEL_RWX) || defined(CONFIG_STRICT_MODULE_RWX)
bool rodata_enabled __ro_after_init = true;
static int __init set_debug_rodata(char *str)
{
	return strtobool(str, &rodata_enabled);
}
__setup("rodata=", set_debug_rodata);
#endif

#ifdef CONFIG_STRICT_KERNEL_RWX
static void mark_readonly(void)
{
	if (rodata_enabled) {
		/*
		 * load_module() results in W+X mappings, which are cleaned up
		 * with call_rcu_sched().  Let's make sure that queued work is
		 * flushed so that we don't hit false positives looking for
		 * insecure pages which are W+X.
		 */
		rcu_barrier_sched();
		mark_rodata_ro();
		rodata_test();
	} else
		pr_info("Kernel memory protection disabled.\n");
}
#else
static inline void mark_readonly(void)
{
	pr_warn("This architecture does not have kernel memory protection.\n");
}
#endif

/arch/arm64/mm/mmu.c

__start_rodata ~ __init_begin 메모리 구간을 read-only(PAGE_KENRL_RO) 속성으로 변경한다.

PAGE_KERNEL 로 변경하면 쓰기가 가능해진다.

void mark_rodata_ro(void)
{
	unsigned long section_size;

	/*
	 * mark .rodata as read only. Use __init_begin rather than __end_rodata
	 * to cover NOTES and EXCEPTION_TABLE.
	 */
	section_size = (unsigned long)__init_begin - (unsigned long)__start_rodata;
	update_mapping_prot(__pa_symbol(__start_rodata), (unsigned long)__start_rodata,
			    section_size, PAGE_KERNEL_RO);

	debug_checkwx();
}
더보기

아래는 /arch/arm64/kernel/smp.c에서 호출하는 함수

linear alias _text ~ __init_begin 메모리 구간을 read-only 속성으로 변경한다.

linear alias는 read-only이어도 rodata 변조에는 영향이 없다.

void __init mark_linear_text_alias_ro(void)
{
	/*
	 * Remove the write permissions from the linear alias of .text/.rodata
	 */
	update_mapping_prot(__pa_symbol(_text), (unsigned long)lm_alias(_text),
			    (unsigned long)__init_begin - (unsigned long)_text,
			    PAGE_KERNEL_RO);
}

rodata 영역 기본 속성은 RW이므로, mark_rodata_ro() 의 update_mapping_prot()을 제거하면 lkm(Loadable kernel module)에서 rodata 영역의 변조가 가능해진다.

unsigned long *start_addr = (unsigned long*)kallsyms_lookup_name("__start_rodata");
*start_addr = 0x1;

unsigned long *sys_call_table = (unsigned long*)kallsyms_lookup_name("sys_call_table");
sys_call_table[__NR_openat] = hook_open;

main.c에서 호출하는 rcu_barrier_sched() 가 어떤 역할을 하는지 확인하지 않았으므로, rodata_enabled 변수를  false 로 변경하는게 깔끔해보인다.

참고 : 많은 인터넷 예제 소스들에서 rodata의 속성 변경을 위해 사용하는 update_mapping_prod() 는 mmu.c의 static 함수이므로 호출이 불가능하다. (로그를 넣고 확인해 본 결과 호출되지 않았음)

void (*update_mapping_prot)(phys_addr_t phys, unsigned long virt, phys_addr_t size, pgprot_t prot);
// 주소는 정상적으로 얻어옴
update_mapping_prot = (void *)kallsyms_lookup_name("update_mapping_prot"); 
unsigned long *start_addr = (unsigned long*)kallsyms_lookup_name("__start_rodata");
// 아래 코드는 실행되지 않으므로, 메모리 속성 변경이 안되어 fault 발생
update_mapping_prot(__pa_symbol(start_addr) , (unsigned long)start_addr, 0x1000, PAGE_KERNEL);
*start_addr = 0x1;

 

위와 같이 커널을 수정하지 않아도, 메모리 속성 변조 함수인 set_memory_rw()를 직접 구현하면 우회가 가능하다.

우선, 커널 소스의 set_memory_rw 구현 부분을 보자.

/arch/arm64/mm/pageattr.c

struct page_change_data {
	pgprot_t set_mask;
	pgprot_t clear_mask;
};

static int change_page_range(pte_t *ptep, pgtable_t token, unsigned long addr,
			void *data)
{
	struct page_change_data *cdata = data;
	pte_t pte = *ptep;

	pte = clear_pte_bit(pte, cdata->clear_mask);
	pte = set_pte_bit(pte, cdata->set_mask);

	set_pte(ptep, pte);
	return 0;
}

/*
 * This function assumes that the range is mapped with PAGE_SIZE pages.
 */
static int __change_memory_common(unsigned long start, unsigned long size,
				pgprot_t set_mask, pgprot_t clear_mask)
{
	struct page_change_data data;
	int ret;

	data.set_mask = set_mask;
	data.clear_mask = clear_mask;

	ret = apply_to_page_range(&init_mm, start, size, change_page_range,
					&data);

	flush_tlb_kernel_range(start, start + size);
	return ret;
}

static int change_memory_common(unsigned long addr, int numpages,
				pgprot_t set_mask, pgprot_t clear_mask)
{
	unsigned long start = addr;
	unsigned long size = PAGE_SIZE*numpages;
	unsigned long end = start + size;
	struct vm_struct *area;

	if (!PAGE_ALIGNED(addr)) {
		start &= PAGE_MASK;
		end = start + size;
		WARN_ON_ONCE(1);
	}

	/*
	 * Kernel VA mappings are always live, and splitting live section
	 * mappings into page mappings may cause TLB conflicts. This means
	 * we have to ensure that changing the permission bits of the range
	 * we are operating on does not result in such splitting.
	 *
	 * Let's restrict ourselves to mappings created by vmalloc (or vmap).
	 * Those are guaranteed to consist entirely of page mappings, and
	 * splitting is never needed.
	 *
	 * So check whether the [addr, addr + size) interval is entirely
	 * covered by precisely one VM area that has the VM_ALLOC flag set.
	 */
	area = find_vm_area((void *)addr);
	if (!area ||
	    end > (unsigned long)area->addr + area->size ||
	    !(area->flags & VM_ALLOC))
		return -EINVAL;

	if (!numpages)
		return 0;

	return __change_memory_common(start, size, set_mask, clear_mask);
}

int set_memory_ro(unsigned long addr, int numpages)
{
	return change_memory_common(addr, numpages,
					__pgprot(PTE_RDONLY),
					__pgprot(PTE_WRITE));
}

int set_memory_rw(unsigned long addr, int numpages)
{
	return change_memory_common(addr, numpages,
					__pgprot(PTE_WRITE),
					__pgprot(PTE_RDONLY));
}

change_memory_common 함수를 보면 find_vm_area를 체크하는 루틴이 보이는데, syscall 테이블의 경우 vm영역이 아니므로 에러가 리턴된다.

체크 루틴을 제거하고 빌드하면 lkm에서 직접 set_memory_rw을 호출할 수 있지만, 위의 코드는 lkm에서도 구현 가능하므로 find_vm_area 체크 루틴만 제거하고 아래와 같이 코드를 간결하게 작성할 수 있다.

struct mm_struct *init_mm_ptr;

struct page_change_data {
    pgprot_t set_mask;
    pgprot_t clear_mask;
};

static int change_page_range(pte_t *ptep, pgtable_t token, unsigned long addr,	void *data)
{
    struct page_change_data *cdata = data;
    pte_t pte = READ_ONCE(*ptep);
    pte = clear_pte_bit(pte, cdata->clear_mask);
    pte = set_pte_bit(pte, cdata->set_mask);
    set_pte(ptep, pte);

    return 0;
}

void set_memory_rw(unsigned long addr, int size)
{
   	struct page_change_data data;
	unsigned long start_addr_align = addr & PAGE_MASK;
	unsigned long end_addr_align = PAGE_ALIGN(addr + size);
	
	int page_size = end_addr_align - start_addr_align;
	data.set_mask = __pgprot(PTE_WRITE);
	data.clear_mask = __pgprot(PTE_RDONLY);
	
	apply_to_page_range(init_mm_ptr, start_addr_align, page_size, change_page_range, &data);
	flush_tlb_kernel_range(start_addr_align, start_addr_align + page_size);
}

void do_it(void)
{
	init_mm_ptr = (struct mm_struct *)kallsyms_lookup_name("init_mm");
	set_memory_rw((unsigned long)sys_call_table, 0x4000);
}

 

 

'Android Linux > Kernel' 카테고리의 다른 글

안드로이드 커널 모듈(lkm) 실행 에러 유형  (0) 2022.04.18

원문 : https://wiki.lineageos.org/devices/gta4xlwifi/install

롬을 빌드하거나 https://download.lineageos.org/gta4xlwifi에서 두 개 다운로드

이미 oem언락 된 상태라면 1~3 과정은 생략

1. adb 디버그 허용, oem 언락 허용(빌드버전 연타하면 나옴)

2. 파워 off 후 볼륨 상 + 볼륨 하(One UI 3.0이상,보통 안드로이드11버전)를 동시에 누르면서, 1초후에 usb를 연결한다. (연결되어있는 상태면 안되고 타이밍 맞게 연결해줘야됨) 잠시 후 다운로드 모드로 진입하게 된다.

볼륨상을 길게 눌러 부트로더 언락을 진행한다.

3. 초기화 완료되면 다시 빌드버전 연타한 후 oem이 언락되어 있는지 확인

4. https://androidfilehost.com/?w=files&flid=304516 heimdall 다운로드

5. 2번처럼 한 후에 언락이 아닌 볼륨 업을 한번만 누른 후 다운로드 모드로 진입. 이후 Zadig를 실행 (https://github.com/pbatard/libwdi/releases)

Options->List All Devices 클릭

반드시 다운로드 모두 진입 후 실행해야 목록에 Samsung USB Composite Device or Gadget Serial이 보임

보이는 것중에 하나 선택하고 Replace Driver를 클릭

만약 Gadget Serial이 두 개 보인다면 둘다 해보는 수 밖에 없음

6. cmd 실행

heimdall print-pit

장치가 재부팅 되는지 확인

7. usb 연결 상태에서 다운로드 모드 진입 후(5번 참고) cmd 실행

heimdall flash --RECOVERY lineage-18.1-20220413-recovery-gta4xlwifi.img --no-reboot

8.  볼륨 다운 + 파워를 8~10초간 누르고 있다가 손 뗌

9. 재부팅 되면 usb연결 후 볼륨 업 + 파워로 리커버리 모드 진입  

(녹스 워런티 깨졌다고 경고 창 뜨면 그 상태에서 볼륨 업 + 파워 길게 눌러주면 됨)

10. Factory Reset -> 3개 전부 실행

11. 메인 메뉴로 이동 후 Apply update-> apply from adb 선택 후 cmd 실행

adb sideload lineage-18.1-20220413-nightly-gta4xlwifi-signed.zip

adb sideload open_gapps-arm64-11.0-pico-20220215.zip (https://opengapps.org/ 에서 다운로드)

adb sidelaod Magisk-v21.4.zip (https://github.com/topjohnwu/Magisk/releases/download/v21.4/Magisk-v21.4.zip 다른 버전 받으면 안돼서 고생 좀 할것임)

adb shell에서만 su가 필요하다면, Magisk를 설치하지 말고 개발자 옵션에서 "디버깅 시 루트 권한 사용" 체크 후 adb root -> adb shell로 접속

12. 재부팅

이 방식을 사용하면 바로 태블릿에 설치 가능한 형태로 빌드된다.

커널 소스 혹은 defconfig를 수정한 후 사용하고자 할 때 유용하다

빌드 방법 원문 : https://wiki.lineageos.org/devices/gta4xlwifi/build

0. vm에 ubuntu 18.04.6 설치 (하드 용량은 넉넉히 500기가 정도)

200기가로 빌드 불가능했음

램은 20기가 정도 설정

1. 의존 패키지 설치

sudo apt-get install openssh-server libwxgtk3.0-dev bc bison build-essential ccache curl python flex g++-multilib gcc-multilib git gnupg gperf imagemagick lib32ncurses5-dev lib32readline-dev lib32z1-dev liblz4-tool libncurses5 libncurses5-dev libsdl1.2-dev libssl-dev libxml2 libxml2-utils lzop pngcrush rsync schedtool squashfs-tools xsltproc zip zlib1g-dev vim

2. adb 설치

https://dl.google.com/android/repository/platform-tools-latest-linux.zip

unzip platform-tools-latest-linux.zip -d ~

~/.profile에 아래 추가

# add Android SDK platform tools to path
if [ -d "$HOME/platform-tools" ] ; then
    PATH="$HOME/platform-tools:$PATH"
fi

3. 디렉토리 생성

mkdir -p ~/bin
mkdir -p ~/android/lineage

4. repo 설치

curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo
git config --global user.email "abc@lol.com"
git config --global user.name "kim kim"

5.

export USE_CCACHE=1
export CCACHE_EXEC=/usr/bin/ccache

6. ~/.bashrc 에 아래 추가 

ccache -M 50G

source ~/.profile 실행

7. 리니지OS 소스 전체 다운로드 (OS+소스 =  기가)

cd ~/android/lineage
repo init -u https://github.com/LineageOS/android.git -b lineage-18.1
repo sync

8. 갤럭시 탭 S6용 커널 다운로드

source build/envsetup.sh
breakfast gta4xlwifi

중간에 vendor 어쩌고 Makefile에러가 발생하면 9번 항목 실행후 breakfast 재시도

(대부분의 makefile에러는 make clean과 breakfast반복으로 해결 가능)

breakfast중 sepolicy.mk 에러가 발생한다면

cd ~/android/lineage/device
mkdir samsung_slsi
cd samsung_slsi
git clone https://github.com/LineageOS/android_device_samsung_slsi_sepolicy
mv android_device_samsung_slsi_sepolicy sepolicy

이 후 make clean 후 breakfast 재시도

9. ubuntu vm에 태블릿 연결 후 태블릿에서 충전이 아닌 PTP로 설정

주의 : adb shell에 su권한이 없으면 파일 복사를 실패함 -> 개발자 옵션에서 디버깅 시 루트 권한 사용 반드시 체크

cd ~/android/lineage/device/samsung/gta4xlwifi
./extract-files.sh

10. 커널 보호 기능 해제

~/android/lineage/kernel/samsung/gta4xl/arch/arm64/configs​/exynos9611-gta4xlwifi_defconfig 편집

아래의 각 라인을 찾아 y를 n으로 수정

(그외 참조 : https://kernsec.org/wiki/index.php/Kernel_Self_Protection_Project/Recommended_Settings)

CONFIG_EXYNOS_KERNEL_PROTECTION=y  
CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y
CONFIG_STRICT_KERNEL_RWX=y
CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y
CONFIG_STRICT_MODULE_RWX=y
CONFIG_EXYNOS_CONTENT_PATH_PROTECTION=y
CONFIG_HAVE_ARCH_SECCOMP_FILTER=n

https://codetronik.tistory.com/155 참조하여 소스도 수정하여야 한다.

11. 빌드

cd ~/android/lineage
source build/envsetup.sh (커널소스 재빌드 시)

brunch gta4xlwifi

12. 출력 파일 확인

cd $OUT

존재 여부 확인 : recovery.img / lineage-18.1-20220418-UNOFFICIAL-gta4xlwifi.zip 

13. 태블릿에 롬 설치 후 adb shell 접속 후 dmesg > grep exynos로 아래의 로그가 뜨는지 확인 (뜨면 defconfig 수정 실패한 것임)

[    0.371387] exynos_protect_kernel_text: Kernel text start VA(0xffffff8008188000), PA(0x80188000)
[    0.371398] exynos_protect_kernel_text: Kernel text end VA(0xffffff8008e28000), PA(0x80e28000)

에러 유형은 dmesg에서 확인할 수 있다.

./insmod 1.ko

1) 에러 유형: 커널 컴파일 시 컴파일러를 android-aach64를 사용하여야 한다. android용이 아니면 에러가 발생한다.

2) 에러 유형: 커널 모듈과 커널 버전이 완전히 일치하여야 한다.

- lkm 버전 확인 : modinfo [lkm.ko]

- 리눅스 커널 버전 확인 : cat /proc/version

insmod: failed to load 1.ko: Invalid argument

(dmesg) 1.ko : disagrees about version of symbol printk

(demsg) 1.ko : Unknown symbol printk (err -22)

 

에러 유형: 커널 모듈과 커널 버전이 완전히 일치하여야 한다.

insmod: failed to load 1.ko: Exec format error

(dmesg) 1.ko : disagrees about version of symbol module_layout

 

에러 유형 : 삼성 단말기에선 LKM을 허용하지 않음 -> 이 경우, 우회하거나 커스텀 os를 새로 설치하여야 한다.

insmod: failed to load 1.ko: Exec format error

(dmesg) LKM is not allowed by Samsung security policy.

0. ubuntu 20.04.4 LTS x64

1. 의존 패키지 설치 Install Dependent Packages (defconfig 및 menuconfig 빌드를 위함)
sudo apt-get install build-essential libncurses5-dev python

2. 커널 소스 다운로드 Download the appropriate kernel source for your device.
Galaxy Tab S6 Lite : git clone https://github.com/LineageOS/android_kernel_samsung_gta4xl

3. 크로스 컴파일러 다운로드 후 경로 설정 Set path after downloading cross-compiler
git clone https://github.com/Shubhamvis98/toolchains

export PATH="/home/code/toolchains/clang-r428724/bin:/home/code/toolchains/aarch64-linux-android-4.9/bin:$PATH"
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-android-
export CLANG_TRIPLE=aarch64-linux-gnu-

4. 커널 소스 디렉토리로 이동 Go to the kernel source path
make clean
make mrproper
make exynos9611-gta4xlwifi_defconfig
make menuconfig  -> 실행 후 <Save> 클릭 

5. 컴파일 Build it
make -j16 CC=clang

6. 커널 소스 수정 (오류 발생 시) Modifying kernel sources (in case of error)
Open Makefile


KBUILD_CFLAGS   := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
   -fno-strict-aliasing -fno-common -fshort-wchar \
   -Werror-implicit-function-declaration \
   -Wno-format-security \
   -Werror \    <------------- 해당 라인 삭제 delete this line
   -std=gnu89

    private boolean checkRootMethod1() {
        for (String str : new String[]{"/system/app/Superuser.apk", "/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su", "/system/bin/failsafe/su", "/data/local/su"}) {
            if (new File(str).exists()) {
                return true;
            }
        }
        return false;
    }

exist() -> /apex/com.android.art/lib64/libjavacore.so에서 access()를 호출함

access() 후킹 코드 작성으로 우회 가능

int (*Org_access)(const char* __path, int __mode);
int hook_access(const char* __path, int __mode)
{
    register uint64_t result;

    __asm volatile ("MOV %0, LR\n" : "=r" (result));

    LOG("access path - %s %lx", __path, result);
    if (strstr(__path, "su"))
    {
        return -1;
    }
    return Org_access(__path, __mode);
}
A64HookFunction((void*)access, (void*)hook_access, (void**)&Org_access);

https://github.com/codetronik/AArch64Hook_android

+ Recent posts