템플릿 구문을 넣어본 결과에 따라 SSTI(Server-Side Template Injection)가 되는지 여부를 판단할 수 있다.

본 문제는 flask로 구동되기 때문에 템플릿 구문으로 {{}}를 사용한다.

{{1*1}}을 넣어 봤을 때,
{{1*1}} 페이지를 찾을 수 없다면 SSTI가 안 되는 것이고,
1 페이지를 찾을 수 없다면 SSTI가 되는 것이다.

본 문제에서는 1 페이지를 찾을 수 없다고 하기에 SSTI에 취약한 것을 알 수 있다.
또한 플래그는 app.secret_key에 저장되어 있기 때문에 어플리케이션 설정 값({{config}})을 통해 확인할 수 있을 것이다.

{{config}} 를 넣어보면, SECRET_KEY 변수에 플래그가 저장되어 있는 것을 확인할 수 있다.

Reference

[1] https://me2nuk.com/SSTI-Vulnerability/
: SSTI에 대한 다양한 정보가 잘 정리되어 있다.

'0x40 Web Hacking' 카테고리의 다른 글

Dreamhack :: devtools-sources  (0) 2022.05.17
Dreamhack :: simple_sqli  (0) 2022.05.17
Dreamhack :: session-basic  (0) 2022.05.17
Dreamhack :: Carve Party  (0) 2022.05.17

selenium을 이용할 때면, webdriver로 chromedriver를 많이 사용하곤 했다. GUI가 필요없을 때에도 headless로 사용했으니 대부분이라 말할수도 있을 것 같다.

하지만 어느 순간부터 selenium을 이용하여 Google login을 자동화하는 부분을 작성하다 보면, 아래와 같은 page를 확인할 수 있다.


문제 파악

email 입력 부분을 채우고 다음 버튼을 누르게 되면, 위와 같은 브라우저 또는 앱이 안전하지 않을 수 있습니다. 라면서 더 이상의 login을 할 수 없도록 한다.

Google 입장에서는 비정상적인 login 시도 행위를 막는 것은 당연한 일이지만, 자동화가 필요한 입장에서는 여간 불편한 것은 사실이다.

조금만 더 생각해 보면 Google에서 만든 chromedriver을 가지고 비정상 행위를 한다고 했을 때, 이를 탐지하는 방법 역시 쉬울테니 언젠가는 막히리라 생각하곤 했으나 실제로 막히니 난감하기는 했다.

그렇다면 Google은 어떻게 chromedriver을 이용한 login automation을 탐지하는 것이며, undetected_chromedriver은 어떻게 이를 우회하는 것일까? 쉬운 방법으로는 firefox와 같은 다른 브라우저의 드라이버를 이용하는 것이나 궁금증을 해결하기 위하여 undetected_chromedriver에 대하여 분석해 봤다.


우회법 분석

Google이 chromedriver을 이용하는 것인지에 대하여 알 방법은 명확하지 않기에 후자인 undetected_chromedriver가 어떻게 login automation을 가능하게 해주는지에 대하여 분석해 본다.

제일 먼저 간단한 binary diffing부터 진행한다.

(.venv) onsoim@MacBook-Pro undetected_chromedriver % ./chromedriver --version
ChromeDriver 98.0.4758.80 (7f0488e8ba0d8e019187c6325a16c29d9b7f4989-refs/branch-heads/4758@{#972})
(.venv) onsoim@MacBook-Pro undetected_chromedriver % md5 chromedriver
MD5 (chromedriver) = 3e5b46c2463efef54a6a8ecfd61b455a
#################################################################################
(.venv) onsoim@MacBook-Pro Safari % ./chromedriver --version
ChromeDriver 98.0.4758.80 (7f0488e8ba0d8e019187c6325a16c29d9b7f4989-refs/branch-heads/4758@{#972})
(.venv) onsoim@MacBook-Pro Safari % md5 chromedriver
MD5 (chromedriver) = 3f71959e5d8644780ee3dc353ffbdc70

기본적으로 undetected_chromedriver에서 설치되는 chromedriver와 https://chromedriver.storage.googleapis.com에서 받은 chromedriver를 비교해 보았다.

Google APIs에서 받은 chromdriver의 cdc_adoQpoasnfa76pfcZLmcfl가 undetected_chromedriver에서 설치되는 chromedriver에서는 xyx_bakbtmdhmelauoizUBpwcp 로 바뀐 것을 확인할 수 있다.

이 부분이 무엇인지 알기 위해서는 binary의 해당 부분을 직접 확인해 보면 명확해 진다.

아래는 위의 문자열이 사용된 함수의 일부를 발췌한 것이다.

function getPageCache(opt_doc, opt_w3c) {
  var doc = opt_doc || document;
  var w3c = opt_w3c || false;
  // |key| is a long random string, unlikely to conflict with anything else.
  var key = '$cdc_adoQpoasnfa76pfcZLmcfl_';
  if (w3c) {
    if (!(key in doc))
      doc[key] = new CacheWithUUID();
    return doc[key];
  } else {
    if (!(key in doc))
      doc[key] = new Cache();
    return doc[key];
  }
}

cdc_adoQpoasnfa76pfcZLmcfl는 window 객체의 여러 값을 가지고 있는 변수명들의 prefix로 사용되는데, 이 prefix는 각 build version마다 고유한 값을 가지고 있기 때문에 Google에서는 이를 이용하여 chromedriver 임을 쉽게 탐지할 수 있는 것 같다.


undetected_chromedriver

실제로 project에서 binary를 patch하는 부분의 코드는 아래와 같다.

@staticmethod
def gen_random_cdc():
    cdc = random.choices(string.ascii_lowercase, k=26)
    cdc[-6:-4] = map(str.upper, cdc[-6:-4])
    cdc[2] = cdc[0]
    cdc[3] = "_"
    return "".join(cdc).encode()

def patch_exe(self):
    """
    Patches the ChromeDriver binary
    :return: False on failure, binary name on success
    """
    logger.info("patching driver executable %s" % self.executable_path)

    linect = 0
    replacement = self.gen_random_cdc()
    with io.open(self.executable_path, "r+b") as fh:
        for line in iter(lambda: fh.readline(), b""):
        if b"cdc_" in line:
            fh.seek(-len(line), 1)
            newline = re.sub(b"cdc_.{22}", replacement, line)
            fh.write(newline)
            linect += 1
            return linect

Google에서는 prefix를 이용하여 chromedriver 임을 탐지하기 때문에 이를 다른 random한 값으로 바꿔주는 방식으로 탐지를 우회한다.

이를 이용하여 Google login을 automation하는 것은 간단하기에 따로 code를 올려두지는 않겠지만 필요하시면 요청해주세요!

평소 PS를 할 때, 자료구조는 크게 신경 쓰지 않았던 것 같다.

그러다가 이번 Dijkstra 문제를 풀면서 자료구조에 따라 시간초과가 발생할 수 있음을 직접 경험해 보며, 앞으로의 PS에는 자료구조도 한번 더 고민해 보기를 바라며 기록을 남긴다.

 

문제 파악

문제가 된 코드는 Dijkstra 함수 구현 부분이다.

물론 제대로 구현된 코드는 아님을 알고 있다.

순회를 할 때 queue를 만든 자료구조를 제외하고는 똑같은 코드이다.

ArrayList

fun dijkstra(N: Int, X: Int, vertex: Array<ArrayList<Int>>): Array<Int> {
    val INF = 1_000_001

    val dist    = Array(N, { INF })
    val visited = Array(N, { true })
    val queue   = arrayListOf<Int>()

    var s = X
    dist[s] = 0
    queue.add(s)

    do {
        s = queue.removeFirst()
        visited[s] = false

        vertex[s].forEach {
            if (visited[it]) {
                queue.add(it)
                dist[it] = min(dist[it], dist[s] + 1)
            }
        }
    } while (queue.isNotEmpty())

    return dist
}

LinkedList

fun dijkstra(N: Int, X: Int, vertex: Array<ArrayList<Int>>): Array<Int> {
    val INF = 1_000_001

    val dist    = Array(N, { INF })
    val visited = Array(N, { true })
    val queue   = LinkedList<Int>()

    var s = X
    dist[s] = 0
    queue.add(s)

    do {
        s = queue.removeFirst()
        visited[s] = false

        vertex[s].forEach {
            if (visited[it]) {
                queue.add(it)
                dist[it] = min(dist[it], dist[s] + 1)
            }
        }
    } while (queue.isNotEmpty())

    return dist
}

두 코드 모두에서 queue를 사용하지만 각각 ArrayListLinkedList로 차이를 보이며, queue의 맨 처음 것을 제거하고 맨 마지막에 추가하는 동일한 방식의 코드이다.

문제점으로는 ArrayList 로 queue를 구현하였을 때는 시간초과가 발생하였지만, LinkedList 로 queue를 구현했을 때는 시간초과가 발생하지 않았다.

 

원인 분석

두 자료구조(ArrayList, LinkedList)는 Java의 List interface를 구현한 Collections의 일종인데, 내부적인 동작 방식을 보면 차이를 보인다.

ArrayList의 경우 데이터의 변화(추가/삭제)가 발생하면 임시 배열을 생성하는 반면 LinkedList는 노드 간의 연결을 변화시킨다.

 

ArrayList

https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html


ArrayList의 구조를 보면 간단하게 배열을 이용하여 구현된 것을 알 수 있다.

하지만 이러한 방식은 검색에서는 효과적일 수는 있지만, 자료의 추가/삭제에는 효율적이지 못하다.

 


데이터의 추가 및 삭제가 발생하면 기존 배열로부터 임시 배열을 만들어 작업을 수행하기에 최악의 경우에는 O(N)의 시간이 걸린다. 즉, 데이터의 밀고 당기는 작업이 필요하다. 추가로 배열은 동적으로 늘어나는 것이 아니라, 배열이 꽉 찼을 경우 더 큰 배열을 만들어 옮겨야 하기에 loss가 크다.

하지만 검색 차원에서는 배열의 시작 주소에 index만을 더해 data가 존재하는 곳의 주소를 바로 알 수 있기 때문에 O(1)의 시간이 걸린다.

 

LinkedList

https://docs.oracle.com/javase/7/docs/api/java/util/LinkedList.html


LinkedList의 구조를 보면 data를 저장하는 노드간의 연결을 통해 리스트로 구현된 것을 알 수 있다.

하지만 이러한 방식은 자료의 추가/삭제에서는 효과적일 수는 있지만, 검색에는 효율적이지 못하다.

 


데이터의 추가 및 삭제가 발생하면 새로운 노드를 만들거나 삭제하고 next 노드를 갱신하면 된다. 하지만 검색을 하기 위해서는 sequential 하게 접근해야 하기에 최악의 경우에는 O(N)의 시간이 걸린다.

기존 배열로부터 임시 배열을 만들어 작업을 수행하기에 최악의 경우에는 O(N)의 시간이 걸린다. 즉, 데이터의 밀고 당기는 작업이 추가로 필요하다.

 

결론 도출

작업 종류 비교
추가/삭제 ArrayList < LinkedList
검색 ArrayList > LinkedList

List를 구성해야 하는 작업이 많다면 LinkedList 를,

구성된 List에서 검색 작업이 많다면 ArrayList를,

사용하는 것이 좋아 보인다.

'0x30 Algorithm' 카테고리의 다른 글

2진수에서 1 누적합 구하기  (0) 2023.07.04
Testcase 모음집  (0) 2021.11.20

'0x30 Algorithm' 카테고리의 다른 글

2진수에서 1 누적합 구하기  (0) 2023.07.04
[Java/Kotlin] ArrayList v.s. LinkedList  (0) 2022.01.26
W tensorflow/stream_executor/platform/default/dso_loader.cc:59] Could not load dynamic library 'cudart64_101.dll'; dlerror: cudart64_101.dll not found

cuda가 정상적으로 설치 되었음에도 cudart64_101.dll이 없다며 에러가 발생하곤 한다.


nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2019 NVIDIA Corporation
Built on Wed_Oct_23_19:32:27_Pacific_Daylight_Time_2019
Cuda compilation tools, release 10.2, V10.2.89

심지어 10.2 버전의 cuda를 설치했음에도 cudart64_101.dll 즉 10.1 버전의 dll을 요구한다.


이를 위한 간단한 해결법을 기록해 놓는다.

cuda가 설치된 폴더로 들어가서, cudart64_102.dll 파일을 복사해서 cudart64_101.dll로 저장한다.

  • 윈도우의 경우 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2\bin 에서 확인할 수 있다.

'0x50 Machine Learning' 카테고리의 다른 글

Code as Graph  (0) 2022.09.28
  • __ROR__ : ROtate Right (circular rotation to the right)
    • __ROR1__ : uint8
    • __ROR2__ : uint16
    • __ROR3__ : uint24
    • __ROR4__ : uint32
  • __ROL__ : ROtate Left (circular rotation to the left)
    • __ROL1__ : uint8
    • __ROL2__ : uint16
    • __ROL3__ : uint24
    • __ROL4__ : uint32

def __ROL__(num, count, bits=8):
    return ((num << count) | (num >> (bits - count))) & ((0b1<<bits) - 1)

def __ROR__(num, count, bits=8):
    return ((num >> count) | (num << (bits - count))) & ((0b1<<bits) - 1)

Ref : https://www.geeksforgeeks.org/rotate-bits-of-an-integer/

안드로이드 디바이스 내부의 폴더들을 확인하면서

편하게 파일을 옮길 수 있기 때문에,

가끔씩 Android File Transfer 어플리케이션을

이용하곤 합니다.

 

하지만 디바이스의 연결 상태가 불안전한 경우,

연결과 해제를 반복하곤 합니다.

 

문제는 위와 같은 창이 반복해서 생성되며,

작업하고 있는 것을 방해하며,

무엇보다 매우 귀찮게 합니다.

 

따라서 기기의 연결을 탐지하여

Android File Transfer을 자동으로 실행하는 것을 방지하고

필요한 경우에만 직접 실행하여 사용할 수 있도록

분석을 하고 필요한 경우에는 패치를 하겠습니다.

 

[*] 해결 방법은 맨 하단부에 위치합니다.

 

Android File Transfer 어플리케이션을 실행하면,

Android File Transfer 어플리케이션과

Android File Trnasfer Agent라는 어플리케이션도

함께 실행이 됩니다.

 

Android File Transfer Agent

Android File Trnasfer Agent 어플리케이션은

1. 어디서 온 어플리케이션이며
2. 뭐하는 어플리케이션인지

이를 초점으로 분석을 해보겠습니다.

 

어디서 온 어플리케이션인가?

Android File Trnasfer 어플리케이션을 분석하면,

위와 같은 코드를 확인할 수 있으며

이에 대하여 간략하게 설명하자면,

다음과 같습니다.

 

mainBundle(Android File Transfer 어플리케이션)이 설치된 경로에서,

Contents/Helpers에 존재하는 또 다른 번들을,

destinationPath에 복사해서 넣습니다.

 

확인을 해보면, Contents/Helpers에는

Android File Transfer Agent라는 번들이 존재합니다.

 

destinationPathlibraryDestinationPath의 값이며,

 

이 값은 Application Support/Google/Android File Transfer입니다.

 

installPathbundleName의 값이며,

 

이 값은 Android File Transfer Agent입니다.

 

따라서 최종적인 경로는

/Users/{username}/Library/Application Support/Google/Android File Transfer/Android File Transfer Agent가 될 것입니다.

 

뭐하는 어플리케이션인가

Android File Transfer Agent 어플리케이션의 start 함수부터 보자면,

mtp를 이용하여 연결된 장치들의 목록을 가져오는 함수를 호출하며,

 

장치들의 연결에 실패할 경우에는 Failed to detect raw devices를,

장치들이 없는 경우에는 No device found를,

상태 메시지로 지정합니다.

 

그 후 add_device_callback 함수가 호출되며,

 

등록된 장치에 대하여, 스케줄을 관리합니다.

 

정리하자면 Android File Tranfer Agent 어플리케이션은

Android File Transfer이 지속적으로 실행될 수 있도록

관리하는 프로그램입니다.

 

따라서 이 Android File Tranfer Agent 어플리케이션이

실행되지 않으면, Anroid File Transfer 어플리케이션은

주기적으로 실행되지 않을 것입니다.

 

해결법

따라서 해결법은 아래와 같습니다.

  1. Android File Transfer 관련 프로세스 정리
  2. /Users/{username}/Library/Application Support/Google/Android File Transfer에 위치한 Android File Transfer Agent 어플리케이션 삭제
  3. /Applications/Android File Transfer.app/Contents/Helpers에 위치한 Android File Transfer Agent 어플리케이션의 이름 변경 또는 삭제

'0xF0 ETC >  Tips' 카테고리의 다른 글

[ Tips] MOV 변환하기  (0) 2019.09.23
[ Tips] PDF 합치기  (0) 2019.03.09

아까 설명했듯이 엑티비티가 실행되면

처음에 onCreate 메소드가 호출됩니다.

 

onCreate 메소드가 길어서 일부만 가져왔습니다.

코드를 보면

저장된 인스턴스 상태를 가져오며

엑티비티 메인으로 값을 설정하는 등의 초기 설정을 합니다.

 


그다음으로는 onStart 메소드가 호출되어야 합니다.

하지만 onStart 메소드는 보이지 않습니다.

 

아마도 super 클래스의 메소드를 오버라이드 하지 않고

그냥 상속받아서 사용하는 것 같다고 추측하고 있지만,

정확히는 잘 모르겠습니다.

 

이런건 개발을 해보면서 배워야 하는데,

안 해봐서 잘 모르겠습니다.

 

이럴 경우에는 다시 onStart 메소드로 돌아가서,

onCreate 메소드에서 어떤 함수들을 호출하는지 확인해봐야 합니다.

 


onCreate 메소드의 마지막 부분을 확인해 보면,

com/kozhevin/rootchecks/util/CheckTask 엑티비티에서

init 메소드를 호출하고 execute 메소드를 호출합니다.

 


CheckTask 엑티비티에서 init 메소드부터 확인해 보면

중간에 asyncTask를 호출하는 부분이 있습니다.

 

네, 비동기로 뭔가를 하는 것 같습니다.

어플리케이션에서는 엑티비티간의 전환은 보이지 않았으므로

백그라운드로 작업을 하는 것 같습니다.

 


같은 엑티비티 내에서

백그라운드 작업을 하는

doInBackground 메소드입니다.

 

쭉 읽다가 보면

결과값이 비어있는 경우

MeatGrinder 엑티비티의 getInstance 메소드와

isLibraryLoaded 메소드를 호출합니다.

 


MeatGrinder의 isLibraryLoaded 메소드에서는

native-lib이라는 이름의 라이브러리를

로드하는 것을 확인할 수 있으며,

여러 메소드를 native 하게 로드하는 것을 확인할 수 있습니다.

 


해당 라이브러리를 ida로 열어보면,

어플리케이션에서 루팅을 확인하는

키워드들이 보이는 것을 확인할 수 있습니다.

 


자 이제부터가 드디어 본 주제였던

루팅을 탐지하는 방법에 대하여 이야기해보겠습니다.

 

안드로이드에서 루팅을 하게 된다면, 크게

- 루팅 하는 과정에서
- 루팅을 활용하는 과정에서

흔적이 발생합니다.

 

루팅을 탐지할 때는

기본적으로 이러한 흔적으로 루팅을 탐지합니다.

 

예를 들면 매번 루트의 권한을 획득하는 건 귀찮으니깐,

이 과정을 생략하기 위해 su 바이너리를 이용합니다.

하지만 이 바이너리의 존재로 루팅이 탐지될 수도 있습니다.

 

또한 루트 권한을 활용하는

다른 어플리케이션의 흔적으로

루팅이 되었는지 확인할 수 있습니다.

 


가장 처음으로는 build.prop 파일로

루팅이 되었는지를 확인할 수 있습니다.

 

이 build.prop 파일은 제조사에서 제품을 출시할 때

제품에 대한 정보나 build 정보 등을 포함하여 출시합니다.

 

그중에서 ro.build.tags 는

출시 당시에 'release-key'로 되어 있지만,

루팅 과정에서 test-keys 나 dev-keys 같은

다른 키로 변경되는 경우가 있으며,

이 키가 변경되었는지 확인을 통해

루팅을 탐지할 수 있습니다.

 


그 외에도

ro.debuggable, service.adb.root,

ro.secure, sys.initd, ro.build.selinux 등을

확인하여 루팅이 되어 있는지를

확인하기도 합니다.

 


아까 예를 들었던 su 바이너리를 통해서

루팅을 탐지할 수 있다는 것을 기억할 것입니다.

 

근데 혹시 이 su 바이너리가

정확히 뭐하는 바이너리인지 아시나요?

 

su는 switch user 즉 사용자를 변경하는 바이너리로,

인자 없이 단독으로 su가 오게 된다면

기본적으로 root로 인식하여

root로 사용자를 변경할 수 있습니다.

 

따라서 이 바이너리의 존재로 루팅을 탐지하기도 합니다.

 


그다음으로

또 su 바이너리를 찾는 것입니다.

 

오른쪽에 보이는 폴더들의 리스트에서 su 바이너리가 있는지 찾습니다.

 


아까 which su를 통해서도 su 바이너리를 찾았고,

방금 전에는 특정 폴더들에서 su 바이너리가 있는지 찾았습니다.

 

이 둘의 차이점은 무엇일까요?

 


차이점은 환경변수에서

PATH 변수에 등록된 주소에서 차이점이 있습니다.

 

which 명령어의 경우에는

이 PATH 변수에 등록된 환경변수에서

인자로 들어온 이름의 실행파일을 찾습니다.

 

즉 이 PATH 변수에 등록이 안된 경로에 대해서는,

which 명령어로 su 바이너리가 존재하는지 확인할 수 없습니다.

 

따라서 su 바이너리가 자주 있거나,

있을 가능성이 높은 폴더들에 대하여

추가로 확인을 하는 것입니다.

 


그다음으로는 Superuser.apk 어플리케이션의

유무를 확인합니다.

 

이 superuser.apk 는

다른 어플리케이션에서 su를 이용할 수 있는 권한을

관리해주는 어플리케이션으로,

이 어필리케이션에 접근할 수 있는지 확인하는 방식으로,

루팅을 탐지합니다.

 


그다음으로는 busybox의 존재로

루팅을 탐지할 수 있습니다.

 

안드로이드의 경우 리눅스를 기반으로 하여,

그 커널 위에서 동작하는 것은 다 들어보셨을 겁니다.

 

이렇게 리눅스 기반이기는 하지만 완전한 리눅스가 아니기에,

기존에 사용하던 여러 리눅스 명령어를 사용하지 못합니다.

 

따라서 이 busybox를 설치하여

여러 명령어를 사용할 수 있게 합니다.

 

busybox는 여러 스크립트 들을 포함하는 실행파일로,

이 busybox로 여러 명령어들을 대체하여

명령어들을 사용할 수 있는 것처럼 해줍니다.

 


다음은 xposed 관련 파일들로

루팅을 탐지할 수 있습니다.

 

xposed는 일종의 프레임워크로

여러 모듈을 적용하여

시스템이나 다른 응용프로그램의 동작을

변경할 수 있게 해 준다고 합니다.

 

저는 써본 적이 없어서 잘은 모르겠습니다.

 


앞에서 언급한 파일들 외에도

magisk 같은 루팅을 이용하는 파일들이

존재하는지 확인합니다.

 


또한 폴더의 권한으로

루팅 여부를 판단할 수 있습니다.

 

system 폴더를 예로 들어 설명하겠습니다.

system 폴더의 경우 기본적으로 읽기 전용 폴더로,

이는 root 권한으로도 수정할 수 없습니다.

 

이 안에 있는 파일들을 수정하기 위해서는

system 폴더를 remount 하여야 하는데,

이러한 폴더 권한의 변경으로

루팅을 탐지하는 경우도 있습니다.

 


마지막으로 후킹을 탐지하는 방법입니다.

 

후킹이란 프로그램에서 함수, 이벤트, 메시지 등이 호출될 때,

이를 중간에서 낚아채가는 것을 말합니다.

 

이 안드로이드에서 후킹 하는 것에 관련된 부분은

주성이가 더 잘 설명해 줄 것이라 믿고,

저는 후킹으로 뭘 할 수 있는지

정도만 보여드리겠습니다.

 


* 해당 슬라이드의 사진 부분은 공개할 수 없습니다.

 

이것은 어느 메신저에서

자체 프로토콜을 빌드하는 부분의

결과 값을 후킹 한 것입니다.

 

전에 상섭이형 발표에서는

치트엔진으로 하셨다고 들은 것 같은데,

이렇게 프리다로 후킹을 해서도

그 내용을 확인할 수도 있습니다.

 


과제라고는 하는데 그냥 선택사항입니다.

 

첫 번째 과제는 오늘 native root checker에서

루팅을 탐지하는 방법을 소개했는데,

이를 참고하여 루팅 탐지를

우회하는 스크립트를 작성하는 것입니다.

 

두 번째 과제는

오늘 소개한 루팅 탐지 방법 외에도

여러 창의적인 방법들이 존재하는데

그중 하나를 찾아 정리해 보고,

그것은 어떤 식으로 탐지를 하는 건지

공부해 보는 것입니다.

 

할 사람들은 기한 없이 하시면서 질문 주시면 되고,

제출은 onsoim@gmail.com으로 보내주시고 알려주시면 됩니다.

 


그리고 안드로이드 쪽 공부를 한다면 도움이 될 자료들입니다.

 

첫 번째는 아시는 분은 아는 그 발표자료입니다.

물론 내용은 좀 삭제되어 있지만

난독화 쪽으로 흥미로운 내용을

많이 포함하고 있습니다.
한번 쭉 보시고, 나중에 더 공부하고 다시 보셔도 좋을 것 같습니다.

 

두 번째는 아까 간략하게 설명한 엑티비티 라이프사이클에 관한 자료입니다.

정리가 아주 잘 되어 있습니다.

 

이상으로 끝내겠습니다. 감사합니다.

+ Recent posts