안경잡이개발자

728x90
반응형

  Apktool을 이용해 APK 파일을 리버싱한 뒤에 이를 다시 재빌드(Rebuild)할 때 가끔 다음의 오류를 만날 수 있다.

 

brut.androlib.AndrolibException: brut.common.BrutException: could not exec (exit code = 1)

 

  이 오류는 Apktool을 이용해 리패키징을 진행할 때 마주칠 수 있는 오류이다. 이 오류는 대부분 AAPT 관련 이슈로 발생한다. AAPT(Android Asset Packaging Tool)안드로이드에서 사용되는 문자열, 이미지 등의 에셋(Asset)을 효율적으로 컴파일하고 패키징하기 위한 도구이다. 안드로이드 앱을 개발할 때는 별도의 에셋 폴더에 문자열, 이미지를 모아 놓는데 이것이 실제 코드와 함께 동작할 수 있도록 한다.

 

방법 1) 리패키징할 때 AAPT v2 사용 강제하기

 

  2020년 기준으로 최신 앱들은 AAPT v2를 사용하는 경우가 많은데, Apktool의 버전에 따라서 이를 처리하는 방법이 다르다. 예를 들어 Apktool v.2.3.4는 AAPT v1과 v2 바이너리를 모두 묶어서 .jar 파일에 넣는다. 이러한 과정에서 리패키징 오류가 발생할 수 있는데, AAPT v2 사용을 강제한다면 문제가 해결될 가능성이 있다.

 

  필자의 경우 Apktool v.2.4.1을 이용하였으며, 재빌드 과정에서 --use-aapt2 옵션을 넣어주었다. 일단 먼저 디컴파일을 하면 다음과 같이 실행될 것이다. 정상적으로 디컴파일이 완료된 것을 확인할 수 있다.

 

apktool -f d test.apk -o test_aapt_v2

 

 

  이후에 재빌드를 할 때 AAPT v2를 강제하지 않으면 다음과 같은 오류가 발생할 수 있다. 그것도 매우 많은 오류가 한꺼번에 발생할 수 있다. 아마 대부분 아래와 같은 오류를 만나서 헤맬 것이다.

 

apktool -f b test_aapt_v2

 

 

  이때 다음과 같이 AAPT v2 사용을 강제하면, 오류가 해결될 가능성이 있다.

 

apktool -f b test_aapt_v2 --use-aapt2

 

  다만 일부 앱에서는 AAPT v2를 강제하여도 오류가 남아 있거나 새로운 오류가 등장할 수 있다. 필자의 경우에도 여전히 오류가 남아 있어서, 다른 방법을 찾아야 했다. --use-aapt2 옵션을 추가하여 문제가 해결된 사람들은 여기에서 끝내도 된다.

 

 

방법 2) Apktool 버전 바꾸어 보기

 

  Apktool의 버전을 교체해본다. 필자의 경우 Apktool 최신 버전(Latest)인 2.4.1을 이용했는데, 이전 버전인 Apktool 2.3.4를 함께 이용해 보는 것도 방법이다. 포럼의 일부 게시글에서는 디컴파일과 리패키징에서 사용하는 Apktool의 버전을 다르게 하라는 조언이 있으며, 이를 통해 해결했다는 사례를 찾을 수 있었다.

 

  혹은 아예 더 낮은 Apktool 버전을 사용하면 일부 앱에 대해서는 정상 동작한다는 사례를 찾을 수 있었다. 따라서 자신의 앱에 대하여 오류가 발생한다면, 여러 가지 버전의 Apktool을 적용해보자. 참고로 필자의 경우에 이 방법을 사용해도 리패키징에서 실패하였다.

 

방법 3) Java 버전 바꾸어 보기

 

  포럼에서는 32-bit로 설치된 Java를 제거하고, 64-bit Java를 설치했더니 문제가 해결된 사례가 있었다고 한다. 필자의 경우 이건 적용해보지 않았다.

 

방법 4) 디컴파일 과정에서 리소스 제거하기

 

  만약 애초에 발생한 오류가 리소스(Resource) 컴파일 과정에서 발생한 것이라면, 리소스는 제외하고 디코딩을 한다면 나중에 리패키징을 할 때 오류가 발생하지 않는다. 소스코드를 수정하는 과정에서 리소스는 건드리지 않고 Java (smali) 코드만 수정하는 것이 목표인 경우, 이 방법만으로도 오류 없이 분석을 진행할 수 있다.

 

  공식 문서에 따르면 다음과 같이 기술되어 있다.

 

  "This will prevent the decompile of resources. This keeps the resources.arsc intact without any decode. If only editing Java (smali) then this is the recommended action for faster decompile & rebuild."

 

  여기에서 resources.arsc 각종 리소스 정보를 담고 있는 파일이다. 우리가 앱을 수정할 때 리소스 파일을 수정하게 되는 일이 필요할 수 있는데, 이때는 이 파일을 디코딩하여 우리가 분석할 수 있는 형태로 변환해야 한다. 예를 들어 새로운 이미지를 추가하거나, 문자열을 추가할 때 꼭 필요할 것이다. 만약 이런 행위 없이, 취약점 분석이 목적이라면 사실 resources.arsc 디코딩은 필요하지 않을 것이다.

 

  저자의 경우 취약점 분석이 목적이므로, 리소스를 제외하고 디코딩을 수행했다.

 

apktool -f d test.apk -o test_without_resource -r

 

 

  이 경우에 다시 리패키징을 할 때 오류가 발생하지 않는다.

 

apktool b test_without_resource

 

 

  필자는 앱을 분석할 때 리소스를 직접 추가하여 앱을 확장할 목적이 없고, 단순히 smali 코드 수정 및 분석이 주 목적이어서 이러한 방법을 이용해도 문제가 없는 상황이다. 결과적으로 다음과 같이 dist 폴더 안에 APK 파일이 생성된다.

 

 

※ 요약 ※

 

  깃허브와 포럼에서 찾아 본 결과, 해결 가능성이 있다고 제안된 방법들은 다음과 같다.

 

  1. 빌드할 때 --use-aapt2 옵션 넣어서, AAPT v2 사용 강제하기
  2. 빌드할 때 이전 Apktool 버전 사용해보기 (디컴파일 때와 다른 버전으로)
  3. Java 버전 및 운영체제 환경 바꾸어서 다시 빌드하기 (32bit -> 64bit)
  4. 리소스 수정이 필요하지 않은 경우, 디컴파일 할 때 -r 옵션 넣어서 resources.arsc 파일 제외하고 디코딩하기

 

  단, 앱 리소스 수정이 꼭 필요하면 1, 2, 3번 적용해보면서 문제 해결하야 할 것이다. 다만 취약점 분석이 목적이라면 대개 resources.arsc를 갈아 끼울 필요는 없고, smali 코드 수정이 필요한 정도라서 4번으로 진행해도 될 것이다.

728x90
반응형

728x90
반응형

  Apktool은 대표적인 안드로이드 애플리케이션(Android Application) 대상의 리버스 엔지니어링 도구입니다. APK 파일을 분석하여 리소스를 뽑아낼 수 있고(디코딩), 코드를 수정하여 다시 재빌드(Rebuild)할 수도 있습니다. 실제로 XML, 이미지 파일, .dex 파일을 포함하여 안드로이드 앱의 주요 소스를 뽑기 위해 가장 많이 사용되고 있습니다.

 

  ▶ Apktool 다운로드: ibotpeaches.github.io/Apktool/

 

  설치를 위해서 [Install] 페이지로 이동하고, 자신의 운영체제에 맞는 프로그램을 다운로드 받습니다.

 

 

  ▶ Apktool.jar 다운로드: bitbucket.org/iBotPeaches/apktool/downloads/

 

  설명에 나와 있는대로 차근차근 다운로드 및 세팅을 진행하시면 됩니다. 가장 먼저 Apktool은 아래와 같이 다운로드 가능합니다.

 

 

  이때 파일의 버전명을 지우고 이름을 apktool.jar로  바꾸어서 C:\\windows 경로에 두면 됩니다. 그리고 apktool.bat 파일 또한 만들어서 C:\\windows 위치에 둡니다. apktool.bat 파일의 내용은 다음의 경로에서 확인할 수 있습니다.

 

  ▶ apktool.bat 내용: https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/windows/apktool.bat

 

  최종적으로 다음과 같은 형태가 됩니다.

 

 

  이후에 명령 프롬프트를 실행하여 apktool 이라고 입력하시면, 정상적으로 툴이 실행됩니다.

 

 

※ 참고사항 ※

 

  참고로 Apktool은 버전에 따라서 APK 파일을 처리하는 방법이 다를 수 있기 때문에, 가능하면 최신 버전과 가장 많이 사용되는 버전 두 가지를 모두 이용하는 것이 좋습니다. 필자의 경우 최신 버전인 Apktool v2.4.1과 더불어 Apktool v.2.3.4를 같이 사용합니다. 저는 그래서 다음과 같이 apktool_2.3.4.jar 또한 다운로드 받아서 윈도우 폴더에 위치시킵니다.

 

 

  이 경우 직접 java를 이용하여 Apktool을 실행하실 수 있습니다.

 

java -jar C:\Windows\apktool_2.3.4.jar

 

 

※ 디컴파일 하는 방법 ※

 

  디컴파일(Decompile)을 위해 하나의 APK 파일을 준비한 뒤에 다음과 같이 명령어를 입력합니다. 여기에서 d는 특정한 파일을 디코딩(Decoding)하라는 의미입니다. 공식 문서에서는 Decoding이라는 표현을 쓰고 있네요. 참고로 -o 옵션을 넣어서 만들어질 소스 폴더의 경로를 설정할 수 있습니다. -o 옵션을 넣지 않으면 자동으로 앱 이름과 동일한 이름의 폴더가 만들어집니다.

 

apktool d {앱 파일} -o {소스 폴더 경로}

 

 

※ 재빌드 하는 방법 ※

 

  소스코드를 수정한 뒤에 다시 재빌드(Rebuild)할 때는 다음과 같은 명령어를 입력합니다. 여기에서 b는 특정한 폴더를 기준으로 다시 빌드(Build)를 진행한다는 의미입니다. 참고로 -o 옵션을 넣어서 빌드 결과로 만들어질 APK 파일의 경로를 설정할 수 있습니다.

 

apktool b {소스 폴더 경로} -o {앱 파일}

 

  재빌드가 성공적으로 이루어진다면 다음과 같이 APK 파일이 만들어지는 것을 확인할 수 있습니다.

 

 

※ 참고사항 ※

 

  추가적인 Apktool에 대한 사용 방법은 공식 문서를 참고하세요.

 

  ▶ 공식 문서: ibotpeaches.github.io/Apktool/documentation/

728x90
반응형

728x90
반응형

  최근에 방송용/녹화용 무료 소프트웨어로 가장 많이 사용되고 있는 것들로는 OBS, PRISM 등이 있습니다. 개인적으로 PRISM Live Studio은 사용 방법이 간단하고 사용자 친화적이라는 생각이 듭니다. (혹시 더 좋은 방송/녹화용 소프트웨어가 있다면 추천해주세요!) 아무튼 저는 최근에 방송이나 녹화 목적으로 PRISM을 이용하고 있습니다. 처음에는 네이버 밴드에서 강의 목적으로 PRISM을 이용했는데 지금은 이게 제일 편해졌습니다. 참고로 OBS도 무료이고 고급스러운 기능들을 제공합니다. 자기한테 가장 잘 맞는 것을 이용하시면 됩니다.

 

※ PRISM Live Studio 설치 방법 ※

 

  PRISM Live Studio는 아래 경로를 통해 설치하실 수 있습니다. (근데 왜 웹 사이트가 HTTPS가 아니라 HTTP를 쓰고 있는 거지... 기분 탓인가...)

 

  ▶ PRISM 설치 경로: prismlive.com/

 

PRISM Live Studio

멀티 플랫폼 라이브 방송과 다양한 영상 제작 및 편집 기능을 제공하는 크리에이터 툴

www.prismlive.com

 

  웹 사이트에 방문하여 바로 설치를 진행할 수 있습니다. 자신의 운영체제에 맞는 설치 프로그램을 자동으로 제안해 주네요.

 

 

  설치 프로그램을 실행하여 설치를 진행하실 수 있습니다. 기본적으로 [확인] 버튼만 눌러도 복잡한 설정 없이 설치를 진행하실 수 있습니다. 참고로 PRISM Live Studio를 이용하기 위해 회원가입/로그인 과정이 필요할 수 있습니다. 아무튼 하라는 대로만 진행하면 됩니다. 개인적으로 초보자 분들도 쉽게 따라할 수 있는 편입니다.

 

 

  다음과 같이 PRISM이 설치될 경로를 설정하여 설치를 진행할 수 있습니다.

 

 

  이어서 다음과 같이 설치가 진행되는 것을 알 수 있습니다.

 

 

  최종적으로 설치가 끝나면 다음과 같이 [시작] 버튼을 눌러서 시작을 진행할 수 있습니다.

 

 

※ 기본 설정 방법 ※

 

  기본적인 상태는 다음과 같습니다. 먼저 [장면 목록]에 여러 개의 장면이 들어갈 수 있습니다. 사실 기본적으로 주어지는 하나의 장면만 가지고도 웬만한 방송을 진행할 수 있습니다.

 

 

  가장 먼저 [소스 목록]에서 [모니터]를 추가해 보겠습니다. 모니터를 추가하면 현재 우리의 컴퓨터 모니터 화면을 방송/녹화 비디오에 송출할 수 있습니다.

 

 

  다음과 같이 송출하고자 하는 디스플레이를 선택할 수 있습니다. 저는 현재 하나의 모니터를 쓰고 있으며, 모니터 화면 해상도는 1920 X 1080입니다. 이것 그대로 디스플레이 선택이 가능한 것을 알 수 있습니다. 그러면 제 화면에 보이는 내용 전체가 그대로 송출됩니다.

 

 

    또한 마이크를 사용하고자 한다면, 가장 먼저 컴퓨터에 마이크를 연결합니다. 이후에 [마이크/보조] 부분을 우클릭하여 [속성]에 들어가도록 합니다.

 

 

  이어서 자신이 연결한 마이크를 선택하면 됩니다.

 

 

  이제 본격적으로 방송이나 녹화를 진행하기 전에 전체 환경 설정을 진행할 필요가 있습니다. 가장 먼저 선택해야 할 것은 방송/녹화 비디오의 해상도입니다. 최소한 1920 X 1080 (HD Quality) 이상의 해상도를 선택하는 것이 좋습니다. 요즘에는 HD보다 낮은 화질인 경우 저화질 취급을 받기 때문입니다. 참고로 자신의 컴퓨터 사양에 따라서 높은 해상도를 선택하지 못할 수도 있습니다.

 

 

  이후에 가장 중요한 부분을 설정해 주겠습니다. 바로 [방송] 및 [녹화] 부분에서 인코더(Encoder)를 설정하는 것입니다. 저는 일반적으로 다음과 같이 방송과 녹화 목적으로 모두 소프트웨어 (x264)를 선택하여 CPU로 녹화를 진행합니다. 여기에서 소프트웨어 (x264)는 컴퓨터의 CPU를 의미하는데요. 제 컴퓨터는 CPU 성능이 좋은 편이기 때문입니다.

 

 

  참고로 인코더는 소프트웨어와 하드웨어로 나누어지는데, 이때 하드웨어 (QSV)는 내장 그래픽 카드를 이용하는 경우이고, 추가적으로 NVIDIA 그래픽 카드가 있다면 하드웨어 (NVENC) 그래픽 카드도 선택할 수 있을 것입니다. 기본적으로 소프트웨어가 아닌 하드웨어를 선택하게 되면 GPU를 이용하므로 상대적으로 CPU를 적게 사용하여 게임이나 강의 녹화를 진행할 수 있게 됩니다.

 

  단, 자기가 사용하는 프로그램이나 컴퓨터 환경에 따라서 효율적인 선택지가 다를 수 있습니다. 여러 번 선택을 해보면서 가장 수월하게 방송/녹화가 진행되는 경우를 채택하시면 됩니다.

 

 

  예를 들어 게임을 GPU로 돌릴 때는, 녹화 프로그램(PRISM)으로 소프트웨어(CPU)를 선택하는 방식을 사용할 수 있습니다. 혹은 반대로 이용할 수도 있어요. 그래서 녹화된 영상이 뚝뚝 끊기거나 오류가 발생하는 경우에는 여러 번 선택을 해보시면서 가장 좋은 조합을 찾으셔야 합니다.

 

  이제 실제로 방송을 보는 사람 입장에서의 화면을 설정합시다. 이후에 [지금 적용] 화살표 버튼을 눌러서 편집 중인 영상(EDIT) 부분이 실제 방송용 혹은 녹화용 화면으로 나오도록 만드시면 됩니다.

 

 

※ 녹화하는 방법 ※

 

  녹화를 진행할 때에는 [Rec] 버튼을 누르시면 됩니다. 녹화를 마치면 자신이 미리 설정했던 경로에 녹화된 동영상 파일이 저장됩니다.

 

 

※ 실시간 방송 하는 방법 ※

 

  또한 실시간 방송을 진행할 때에는 가장 먼저 [채널 추가]를 누르시면 됩니다.

 

 

  다음과 같이 다양한 채널을 선택할 수 있습니다.

 

 

  참고로 많은 방송 플랫폼은 고유하게 부여되는 스트림 키(Stream Key)만 가지고도 방송을 진행할 수 있도록 합니다. 그래서 스트림 키가 노출된다면, 다른 사람이 제 계정으로 방송을 할 수도 있는 겁니다. 혹은 해당 플랫폼 계정으로 로그인을 한 뒤에 자동으로 PRISM과 연동이 되는 방식으로 동작하는 플랫폼도 존재합니다.

 

  아무튼 유튜브(YouTube)의 경우 다음과 같이 스트림 키를 입력하여 유튜브 채널로 실시간 방송을 송출할 수 있습니다.

 

 

  이후에 동시에 송출하고자 하는 채널들을 선택한 뒤에 [Go Live] 버튼을 누르면 실제로 라이브 방송이 진행됩니다.

 

728x90
반응형

728x90
반응형

(이번 대회는 대회 중간에 문제 오류로 인하여 Unrated 판정 공지가 나왔다. 그래서 A번과 B번만 풀었다.)

 

대회 링크: codeforces.com/contests/1418

A번 문제: codeforces.com/contest/1418/problem/A

 

  하나의 횃불(Torch)을 만들기 위해서는 1개막대(Stick)1개석탄(Coal)이 필요하다.

 

  우리는 처음에 1개의 막대기를 가지고 시작하며, 두 종류의 연산을 사용할 수 있다.

 

  1번) 1개의 막대기를 x개의 막대기로 교환

  2번) y개의 막대기를 1개의 석탄으로 교환

 

  이때 총 k개의 횃불(Torch)을 만들기 위해 필요한 최소 연산 횟수를 구하면 되는 문제다.

 

[ 문제 해설 ]

 

  우리는 먼저 1개의 막대기에서 시작한다. 따라서 1번 연산을 많이 사용해서 막대기의 개수를 최대한 늘린 뒤에, 그 다음에 2번 연산을 많이 사용해서 석탄을 얻어서 결과적으로 횃불을 만들면 된다.

 

  우리가 막대기를 처음에 증가시킬 때, 연산 1번을 사용할 때마다 막대기를 (x - 1)개씩 증가시킬 수 있다.

 

  k개의 횃불을 만들기 위해서는 k개의 막대기와 k개의 석탄이 필요하다. 이때 k개의 석탄을 만들기 위해서는 y * k개의 막대기가 필요하다. 즉, k개의 횃불을 만들기 위해서(y + 1) * k개의 막대기가 필요한 것이다.

 

  그렇다면 (y + 1) * k개 이상의 막대기를 얻기 위해 1번 연산을 얼마나 사용해야 할까? 바로 다음의 부등식을 만족시키는 한에서 최소한으로 사용해야 할 것이다.

 

  ▶ 필요한 1번 연산의 횟수 * (x - 1) + 1 >= (y + 1) * k

 

  따라서 다음의 코드를 통해서 1번 연산의 횟수의 최솟값을 구할 수 있다.

 

if ((y + 1) * k - 1) % (x - 1) == 0:
    one = ((y + 1) * k - 1) // (x - 1)
else:
    one = ((y + 1) * k - 1) // (x - 1) + 1

 

  이제 2번 연산을 k번 수행하면 최종적으로 k개의 횃불을 만들 수 있다. 따라서 최종적인 코드는 다음과 같다.

 

for _ in range(int(input())):
    x, y, k = map(int, input().split())
    if ((y + 1) * k - 1) % (x - 1) == 0:
        one = ((y + 1) * k - 1) // (x - 1)
    else:
        one = ((y + 1) * k - 1) // (x - 1) + 1
    two = k
    print(one + two)

 

B번 문제: codeforces.com/contest/1418/problem/B

 

  N개의 정수를 담고 있는 수열이 있다. N개 중에서 몇 개의 원소는 고정되어(Locked) 위치를 변경할 수 없다. 반면에 고정되어 있지 않은(Unlocked) 원소들은 위치를 서로 변경할 수 있다.

 

  이때 누적 합(Prefix Sum)이 0 미만인 인덱스(index) 중에서 최솟값(Miminum)인 k를 계산해야 한다. 만약 누적합이 0 미만인 인덱스가 없다면 k = 0가 된다.

 

  예를 들어 [-8, 4, -2, -6, 4, 7, 1]이 있다고 해보자. 여기에서 밑줄이 쳐진 원소들은 고정된 원소들이다. 이 경우 [-8, 4, 1, -2, 4, 7, -6]로 수열을 바꾸게 되면, 누적합은 [-8, -4, -3, -5, -1, 6, 0]이다. 이때, 누적합이 0 미만인 인덱스 중에서 최솟값은 k = 5가 된다. (인덱스를 1부터 센다고 했을 때)

 

[ 문제 해설 ]

 

  이 문제는 위치가 고정된 원소를 제외하고, 남은 원소들만 내림차순 정렬을 시켜주면 된다. 그때가 바로 누적 합(Prefix Sum)의 모든 원소가 가장 크게 형성되는 때라고 볼 수 있다.

 

from collections import deque

for _ in range(int(input())):
    n = int(input())
    data = list(map(int, input().split()))
    flag = list(map(int, input().split()))
    
    # 고정되지 않은 원소만 담아서 내림차순 정렬
    temp = []
    for i in range(n):
        if flag[i] == 0:
            temp.append(data[i])
    temp.sort(reverse=True)

    # 최종 결과 리스트 만들기
    result = []
    temp = deque(temp)
    for i in range(n):
        # 고정되지 않은 위치에는 내림차순으로 하나씩 넣기
        if flag[i] == 0:
            result.append(temp.popleft())
        # 고정되어 있다면 그대로 가져오기
        else:
            result.append(data[i])

    # 최종 결과 리스트 출력
    for x in result:
        print(x, end=' ')
    print()
728x90
반응형