번들파일을 암호화하는 이유는 저작권이 있는 리소스를 보호하고 싶다거나 테이블 데이터를 보호하고 싶다거나.. 여러가지 이유가 있습니다. AssetStudio같은걸 사용하면 너무나 쉽게 에셋번들의 데이터를 추출해볼 수 있습니다. 이를 방지하려면 어떻게 해야할까요?
AssetBundle의 파일 사이즈는 100mb가 될 수도 있고, 1GB가 될 수도 있기 때문에 암호화를 할때 주의해야 할점은 파일 전체를 암호화 한다기 보다, 파일의 특정 부분만을 암호화하여 정상적으로 파일이 열리지 않게 하는게 포인트입니다.
위 사진은 큐브 오브젝트를 번들로 만들어서 뽑은 뒤 파일을 헥스 에디터로 열어본 사진입니다. 큐브 오브젝트를 번들로 만들어서 뽑았을때 맨 앞에서 4개의 바이트가 55 6E 69 74 로 시작합니다. 어떻게 번들을 뽑든 번들 파일의 첫 시작 바이트부분은 55 6E 69 74 고정입니다. 이것을 다른 바이트로 변경해서 유니티가 파일을 읽어오지 못하게 하면 암호화 성공입니다. 사실 고정 된 부분이 아닌 파일의 +30 번째 바이트를 바꾼다거나.. 방법은 자기 마음입니다. 저는 55 6e 69 74 라는 바이트배열을 변경해보겠습니다.
먼저, 에셋을 보호하기위해 번들을 다운로드 했을때 파일을 저장시키기 전 암호화 를 해서 한번 저장합니다. 아래 코드는 번들 파일의 맨 앞 시작 바이트 부분을 임의의 바이트 (0x99, 0x94, 0x88, 0x24)로 변경하는 코드입니다.
public IEnumerator DownloadAndEncryptSaveBundle(string BundleURL)
{
// Download the file from the URL. It will not be saved in the Cache
using (WWW www = new WWW(BundleURL))
{
yield return www;
if (www.error != null)
throw new Exception("WWW download had an error:" + www.error);
//다운로드한 바이트를 스트림으로 읽어옴
MemoryStream ms = new MemoryStream(www.bytes);
///www dispose로 기존 www 객체의 메모리는 해제
www.Dispose();
//writer 객체생성
BinaryWriter writer = new BinaryWriter(ms);
//맨 앞의 값을 수정
writer.Seek(0, SeekOrigin.Begin);
writer.Write(new byte[] { 0x99, 0x94, 0x88, 0x24}, 0, 4);
//수정한 바이트를 쓴다.
File.WriteAllBytes("testprefab.encrypt", ms.ToArray());
writer.Close();
ms.Close();
} // memory is freed from the web stream (www.Dispose() gets called implicitly)
yield return null;
}
위 코드로 다운로드하고 파일을 저장하면 기존 번들이 아래와같이 바이트가 바뀝니다.
이제는 번들의 파일 구조가 변경되었으므로 일반적인 방법으로는 이제 열 수가 없지만, 유니티에서도 정상적으로 읽을 수 없기때문에 복호화 로직을 짜야합니다.
public void DecryptAndLoadBundle(string fileName)
{
//파일 스트림 생성
FileStream stream = File.Open(fileName, FileMode.Open);
var length = stream.Length;
var readedBytes = new byte[length];
int numBytesRead = 0;
//파일 스트림으로부터 byte 읽어오기
while (length > 0)
{
// Read may return anything from 0 to numBytesToRead.
int n = stream.Read(readedBytes, numBytesRead, (int)length);
// Break when the end of the file is reached.
if (n == 0)
break;
numBytesRead += n;
length -= n;
}
stream.Close();
MemoryStream ms = new MemoryStream(readedBytes);
BinaryWriter writer = new BinaryWriter(ms);
// 기존 앞 바이트 4자리는 5E 6E 69 74 였으므로 이것으로 다시 돌려준다. (복호화)
writer.Seek(0, SeekOrigin.Begin);
writer.Write(new byte[] { 0x55, 0x6E, 0x69, 0x74 }, 0, 4);
var decryptedBytes = ms.ToArray();
ms.Close();
writer.Close();
var bundle = AssetBundle.LoadFromMemory(decryptedBytes);
var prefab = bundle.LoadAsset<GameObject>("번들 파일 이름");
Instantiate(prefab);
}
정상적으로 파일이 불러와지면 성공입니다. 저는 앞 자리 4자리만 암호화 하는 방식을 사용했는데 예시를 위한것이고, 소스코드를 조금 수정해서 암복호화 하시는게 좋습니다 :)
'프로그래밍 > unity' 카테고리의 다른 글
UniTask 기본 사용법/생명주기 주의사항 (2) | 2021.12.05 |
---|---|
AssetBundle의 암호화/복호화 (0) | 2021.08.04 |
UGS는 유료 에셋으로 전환 될 예정입니다. (0) | 2021.07.22 |
유니티 구글 스프레드시트로 데이터 쉽게 관리하기(UGS) (24) | 2021.03.17 |