r193
r159
1[include(틀:유우리)]
r158
2[include(틀:스텔라이브)]
r163
3[include(틀:Aidenk/틀 모음, Aidenk=0)]
r85
4{{{#!wiki style="max-width: 500px; padding: 10px 0; border-radius: 20px; background: linear-gradient(to right, #ba9dff, #ba9dff); float: right; text-align: center"
r155
5||<-2><tablewidth=100%><table bgcolor=#fff,#1c1d1f><tablebordercolor=#ba9dff,#ba9dff><colcolor=#fff><color=#00d2ff> ||
r175
6||<-2><nopad>[youtube(EIii4EXsfsU)]||
r172
7||{{{#!folding 기존 사진 접기
r176
8[[파일:special gift of kanna.jpg|width=100%]][br][br][[파일:블아임.png|width=100%]][br][br][[파일:규리감.jpg|width=100%]][br][br][[파일:2025/09/27 유니버스 타비/리제 신의상 합방.jpg|width=100%]]}}}[br][[파일:클리셰 공식 단체 사진.jpg|width=100%]]||}}}
r56
9
r23
10[목차]
11[clearfix]
r167
12== 개요 ==
r168
13생방은 본지 몇개월 안된 유튜브로만 보던 1~2년차 파스텔. 초기의 시작은 강지와 김블루 우결. 그렇게 강지를 알게된후 칸나를 알게되어 이렇게 파스텔이 되었다 카더라.
r167
14
r191
15== 통포(10/31) ==
16 1. 위: [[시라유키 히나]] 12,960개
17 1. 위: [[텐코 시부키]] 10,400개
r174
18 1. 위: [[아라하시 타비]] 1,680개
19 1. 위: [[아카네 리제]] 1,560
20 1. 위: [[유즈하 리코]] 1,190
21 1. 위: [[하나코 나나]] 970개
r176
22 1. 위: [[네네코 마시로]] 660개
r174
23 1. 위: [[사키하네 후야]] 590개
24 1. 위: [[아오쿠모 린]] 460개
25 1. 위: [[아야츠노 유니]] 310개
26 1. 위: [[강지]] 300개
r167
27
r170
28파스텔이라고 모든 방송을 다 보는것이 아니다. 사실상 7위 아래로는 거의 안본다. 유튜브도 이런거 만들어야한다.
r169
29
r135
30== 방송 역사 작성 요령 ==
r150
31간단합니다. 치지직에서 생방을 보면됩니다. 히나, 부키는 거기다가 방송에서 노래랑 피아노를 자주 쳐주고 불러주기에 힐링됩니다. 힐링 방송을 원하시다면 파스텔 또는 이나리, 해둥이가 되세요! 새벽 4시에 키는게 어디있냐고! 아... 자고싶다... 아닛! 휴대폰 키고 자버려서 노래방 못봤다...
r135
32
r143
33가끔 x 또는 카페의 방송 후기나 기록을 가지고 작성합니다. 유니, 타비, 시로, 리제, 린, 나나, 리코는 작성이 쉽습니다. 하지만 부키, 히나는 작성이 힘듭니다.
34히나는 "안자는 해둥이 있나요"는 새벽 3시에 끝나서 작성하기 힘듭니다. 하지만 저는 작성하죠 ㅎㅎ 부키는 평균 방송 시간 7시간 거의 새벽까지 방송하기에 히나와 비슷하게 새벽 3시까지는 깨어있어야합니다.
35
r156
36Aidenk의 노가다를 보기 -> [[사용자:Aidenk/연습장]]
r143
37
r147
38Aidenk의 버츄얼 수필을 보기 -> [[사용자:Aidenk/버츄얼]]
39
r23
40== 개요 ==
r140
41해둥이 주글께! - 해둥이 -
r139
42
r120
43주석은 혁명이야!
r119
44
r157
45[[스텔라이브]] 실시간 업데이트 중이진 않음
r145
463기생 사진 준비중 ...
r119
47
r118
48직접 곡을 들으며 starr, end 시간대를 찾는 편집으로 힐링하는 사람. 편집도 하고 노래도 듣고 생방도 보고 너무 좋잖아!
r113
49
r161
50[[나무위키]]의 [[https://namu.wiki/w/%EC%82%AC%EC%9A%A9%EC%9E%90:Aidenk|Aidenk]], [[https://namu.wiki/w/%EC%82%AC%EC%9A%A9%EC%9E%90:Airi_Kanna|Airi_Kanna]], [[https://namu.wiki/w/%EC%82%AC%EC%9A%A9%EC%9E%90:Hebi|Hebi^^{{{-3 봇계정}}}^^]] 와 동일인. 더시드위키의 [[사용자:Tenko_Shibuki]], [[사용자:Shirayuki_Hina]]와 동일인.
r98
51프로필 사진 너무 예쁜데... 예쁘고 귀여우니... 별찜좀...
52
r146
53이나리로 전향한듯 한 해둥이(?)가 아닌데 이나리인데 해둥이. 근데 바람은 아님(?)
r125
54음 그니깐... 쨋든 파스텔.
r105
55
r106
56[[/연습장|{{{#!wiki style="display: inline; color: transparent; background: text linear-gradient(to right, #f58abb, #fed09f)"
r182
57'''연습장 바로가기'''}}}]]
58
r184
59
60== csharp 암호화 코드 ==
r186
61오류가 많을 예정(ai 작성). 최적화 따윈 없는 코드.
r190
62아무리 생각해도 EnableAntiDebugChecks는 오탐지 가능성이 있다. 블랙 리스트에 추가되도록 할지 생각중...
r184
63
r185
64=== 본 코드 ===
r182
65{{{#!syntax csharp
66using System;
67using System.IO;
68using System.Security.Cryptography;
69using System.Text;
70using UnityEngine;
71using System.Diagnostics;
72using System.Runtime.CompilerServices;
73
74[Serializable]
75public class PlayerData
76{
77 public string playerName;
78 public int score;
79}
80
81public class SecureManager : MonoBehaviour
82{
83 public static SecureManager Instance { get; private set; }
84
85 public bool EnableHardening = true;
86 public bool EnableAntiDebugChecks = true;
87 public bool EnableRootDetection = true;
88 public bool EnableDummyMix = true;
r192
89 public bool EnableFileNameObfuscation = false;
r182
90 public bool EnableRotation = true;
91
92 private const string Magic = "SJMP";
93 private const byte Version = 1;
94 private const int SaltLenDefault = 12;
95 private const int IvLen = 16;
96 private const int HmacLen = 32;
97 private const int KeyMaterialLen = 64;
98 private const int AesKeyLen = 32;
99 private const int HmacKeyLen = 32;
100 private const int Pbkdf2Iterations = 20000;
101 private readonly byte[] obfPartA = new byte[] { 0x4A, 0x5F, 0x33, 0x29, 0x11, 0x7C, 0x6D, 0x3E, 0x2D, 0x7A, 0x5B, 0x1C };
102 private readonly byte[] obfPartB = new byte[] { 0x91, 0x20, 0x55, 0x12, 0x44, 0x38, 0x77, 0x0A, 0x6F, 0x21, 0x9D, 0xEE };
103 private const byte Mask = 0xAA;
104 private string filePath;
105 private byte[] sessionNonce;
106
107 void Awake()
108 {
109 if (Instance != null && Instance != this)
110 {
111 Destroy(gameObject);
112 return;
113 }
114 Instance = this;
115 DontDestroyOnLoad(gameObject);
116
117 sessionNonce = GenerateRandomBytes(16);
118 filePath = Path.Combine(Application.persistentDataPath, ResolveFileName("playerData"));
119 Warmup();
120 }
121
122 private void Warmup()
123 {
124 try { _ = GetSecretBytes(); } catch { }
125 }
126
127 [MethodImpl(MethodImplOptions.NoInlining)]
128 private byte[] DumbMixA(byte[] input)
129 {
130 byte[] outb = new byte[input.Length];
131 for (int i = 0; i < input.Length; i++)
132 {
133 int v = input[i];
134 v = ((v << 3) | (v >> 5)) & 0xFF;
135 v ^= (i * 37) & 0xFF;
136 outb[i] = (byte)v;
137 }
138 return outb;
139 }
140
141 [MethodImpl(MethodImplOptions.NoInlining)]
142 private byte[] DumbMixB(byte[] input, int seed)
143 {
144 byte[] outb = new byte[input.Length];
145 for (int i = 0; i < input.Length; i++)
146 {
147 int v = input[i] ^ (seed >> (i % 8));
148 v = (v * 13 + (i * 7)) & 0xFF;
149 v = (v ^ 0x5A) & 0xFF;
150 outb[i] = (byte)v;
151 }
152 return outb;
153 }
154
155 [MethodImpl(MethodImplOptions.NoInlining)]
156 private byte[] DumbMixC(byte[] input, byte[] nonce)
157 {
158 byte[] outb = new byte[input.Length];
159 for (int i = 0; i < input.Length; i++)
160 {
161 int n = nonce[i % nonce.Length];
162 int v = input[i];
163 v = ((v + n) ^ (n >> (i % 4))) & 0xFF;
164 outb[i] = (byte)v;
165 }
166 return outb;
167 }
168
169 private byte[] GetSecretBytes()
170 {
171 byte[] k = new byte[obfPartA.Length + obfPartB.Length];
172 for (int i = 0; i < obfPartA.Length; i++) k[i] = (byte)(obfPartA[i] ^ Mask);
173 for (int i = 0; i < obfPartB.Length; i++) k[i + obfPartA.Length] = (byte)(obfPartB[i] ^ Mask);
174
175 if (!EnableHardening || !EnableDummyMix)
176 {
177 byte[] simple = new byte[k.Length];
178 Buffer.BlockCopy(k, 0, simple, 0, k.Length);
179 Array.Clear(k, 0, k.Length);
180 return simple;
181 }
182
183 byte[] s1 = DumbMixA(k);
184 int dynamicSeed = (int)(DateTime.UtcNow.Ticks ^ Application.identifier.GetHashCode() ^ Environment.TickCount);
185 byte[] s2 = DumbMixB(s1, dynamicSeed);
186 byte[] s3 = DumbMixC(s2, sessionNonce);
187 for (int r = 0; r < 2; r++)
188 {
189 byte[] t = DumbMixA(s3);
190 byte[] u = DumbMixB(t, dynamicSeed ^ r);
191 for (int i = 0; i < k.Length; i++) s3[i] = (byte)(s3[i] ^ u[i % u.Length]);
192 Array.Clear(t, 0, t.Length);
193 Array.Clear(u, 0, u.Length);
194 }
195 Array.Clear(s1, 0, s1.Length);
196 Array.Clear(s2, 0, s2.Length);
197 byte[] result = new byte[k.Length];
198 for (int i = 0; i < k.Length; i++) result[i] = (byte)(k[i] ^ s3[i % s3.Length]);
199 Array.Clear(k, 0, k.Length);
200 Array.Clear(s3, 0, s3.Length);
201 return result;
202 }
203
204 private (byte[] aesKey, byte[] hmacKey) DeriveKeysRotating(byte[] salt, byte[] rotationNonce)
205 {
206 byte[] secret = GetSecretBytes();
207 string secretStr = EnableHardening ? Convert.ToBase64String(secret) + Application.identifier : Encoding.UTF8.GetString(secret) + Application.identifier;
208 Array.Clear(secret, 0, secret.Length);
209
210 using (var kdf = new Rfc2898DeriveBytes(secretStr, salt, Pbkdf2Iterations, HashAlgorithmName.SHA256))
211 {
212 byte[] km = kdf.GetBytes(KeyMaterialLen);
213 if (EnableHardening && EnableRotation && rotationNonce != null)
214 {
215 for (int i = 0; i < km.Length; i++) km[i] ^= rotationNonce[i % rotationNonce.Length];
216 }
217 byte[] aesKey = new byte[AesKeyLen];
218 byte[] hmacKey = new byte[HmacKeyLen];
219 Buffer.BlockCopy(km, 0, aesKey, 0, AesKeyLen);
220 Buffer.BlockCopy(km, AesKeyLen, hmacKey, 0, HmacKeyLen);
221 Array.Clear(km, 0, km.Length);
222 return (aesKey, hmacKey);
223 }
224 }
225
226 public void Save<T>(T data)
227 {
228 if (EnableHardening && (EnableAntiDebugChecks || EnableRootDetection) && IsTamperedOrDebug()) return;
229 try
230 {
231 string json = JsonUtility.ToJson(data);
232 byte[] salt = GenerateRandomBytes(SaltLenDefault);
233 byte[] rotation = (EnableHardening && EnableRotation) ? GenerateRandomBytes(16) : new byte[16];
234 var keys = DeriveKeysRotating(salt, (EnableRotation ? rotation : null));
235 byte[] aesKey = keys.aesKey;
236 byte[] hmacKey = keys.hmacKey;
237 byte[] plain = Encoding.UTF8.GetBytes(json);
238 byte[] iv;
239 byte[] cipher;
240 using (Aes aes = Aes.Create())
241 {
242 aes.KeySize = 256;
243 aes.Mode = CipherMode.CBC;
244 aes.Padding = PaddingMode.PKCS7;
245 aes.Key = aesKey;
246 aes.GenerateIV();
247 iv = aes.IV;
248 using (var enc = aes.CreateEncryptor(aes.Key, iv))
249 cipher = enc.TransformFinalBlock(plain, 0, plain.Length);
250 }
251 byte[] ivCipher = new byte[iv.Length + cipher.Length];
252 Buffer.BlockCopy(iv, 0, ivCipher, 0, iv.Length);
253 Buffer.BlockCopy(cipher, 0, ivCipher, iv.Length, cipher.Length);
254 byte[] hmac;
255 using (var h = new HMACSHA256(hmacKey)) hmac = h.ComputeHash(ivCipher);
256 using (MemoryStream ms = new MemoryStream())
257 {
258 ms.Write(Encoding.ASCII.GetBytes(Magic), 0, 4);
259 ms.WriteByte(Version);
260 ms.WriteByte((byte)salt.Length);
261 ms.Write(salt, 0, salt.Length);
262 ms.WriteByte(EnableRotation ? (byte)rotation.Length : (byte)0);
263 if (EnableRotation) ms.Write(rotation, 0, rotation.Length);
264 ms.Write(iv, 0, iv.Length);
265 byte[] cipherLenBytes = BitConverter.GetBytes((Int32)cipher.Length);
266 if (!BitConverter.IsLittleEndian) Array.Reverse(cipherLenBytes);
267 ms.Write(cipherLenBytes, 0, 4);
268 ms.Write(cipher, 0, cipher.Length);
269 ms.Write(hmac, 0, hmac.Length);
270 string outBlob = Convert.ToBase64String(ms.ToArray());
271 File.WriteAllText(filePath, outBlob);
272 }
273 ClearBytes(aesKey);
274 ClearBytes(hmacKey);
275 ClearBytes(plain);
276 ClearBytes(iv);
277 ClearBytes(cipher);
278 ClearBytes(ivCipher);
279 ClearBytes(hmac);
280 ClearBytes(salt);
281 ClearBytes(rotation);
282 }
283 catch { }
284 }
285
286 public T Load<T>()
287 {
288 if (EnableHardening && (EnableAntiDebugChecks || EnableRootDetection) && IsTamperedOrDebug()) return default;
289 if (!File.Exists(filePath)) return default;
290 string blob = File.ReadAllText(filePath);
291 byte[] total;
292 try { total = Convert.FromBase64String(blob); } catch { return default; }
293 try
294 {
295 using (MemoryStream ms = new MemoryStream(total))
296 using (BinaryReader br = new BinaryReader(ms))
297 {
298 byte[] magic = br.ReadBytes(4);
299 if (Encoding.ASCII.GetString(magic) != Magic) return default;
300 byte ver = br.ReadByte();
301 if (ver != Version) return default;
302 int saltLen = br.ReadByte();
303 if (saltLen <= 0 || saltLen > 64) return default;
304 byte[] salt = br.ReadBytes(saltLen);
305 int rotationLen = br.ReadByte();
306 byte[] rotation = rotationLen > 0 ? br.ReadBytes(rotationLen) : new byte[16];
307 byte[] iv = br.ReadBytes(IvLen);
308 int cipherLen = br.ReadInt32();
309 if (!BitConverter.IsLittleEndian) cipherLen = System.Net.IPAddress.NetworkToHostOrder(cipherLen);
310 if (cipherLen <= 0 || cipherLen > total.Length) return default;
311 byte[] cipher = br.ReadBytes(cipherLen);
312 byte[] hmac = br.ReadBytes(HmacLen);
313 byte[] ivCipher = new byte[iv.Length + cipher.Length];
314 Buffer.BlockCopy(iv, 0, ivCipher, 0, iv.Length);
315 Buffer.BlockCopy(cipher, 0, ivCipher, iv.Length, cipher.Length);
316 var keys = DeriveKeysRotating(salt, (EnableRotation ? rotation : null));
317 byte[] aesKey = keys.aesKey;
318 byte[] hmacKey = keys.hmacKey;
319 byte[] expected;
320 using (var h = new HMACSHA256(hmacKey)) expected = h.ComputeHash(ivCipher);
321 bool ok = CryptographicOperations.FixedTimeEquals(expected, hmac);
322 if (!ok)
323 {
324 ClearBytes(aesKey);
325 ClearBytes(hmacKey);
326 ClearBytes(expected);
327 return default;
328 }
329 byte[] plain;
330 using (Aes aes = Aes.Create())
331 {
332 aes.KeySize = 256;
333 aes.Mode = CipherMode.CBC;
334 aes.Padding = PaddingMode.PKCS7;
335 aes.Key = aesKey;
336 aes.IV = iv;
337 using (var dec = aes.CreateDecryptor(aes.Key, aes.IV))
338 plain = dec.TransformFinalBlock(cipher, 0, cipher.Length);
339 }
340 string json = Encoding.UTF8.GetString(plain);
341 ClearBytes(aesKey);
342 ClearBytes(hmacKey);
343 ClearBytes(expected);
344 ClearBytes(plain);
345 ClearBytes(iv);
346 ClearBytes(cipher);
347 ClearBytes(salt);
348 ClearBytes(rotation);
349 return JsonUtility.FromJson<T>(json);
350 }
351 }
352 catch { return default; }
353 }
354
355 private static byte[] GenerateRandomBytes(int len)
356 {
357 byte[] b = new byte[len];
358 using (var rng = RandomNumberGenerator.Create()) rng.GetBytes(b);
359 return b;
360 }
361
362 private static void ClearBytes(byte[] b)
363 {
364 if (b == null) return;
365 Array.Clear(b, 0, b.Length);
366 }
367
368 private string ResolveFileName(string baseName)
369 {
370 if (!EnableHardening || !EnableFileNameObfuscation) return baseName + ".dat";
371 byte[] nameBytes = Encoding.UTF8.GetBytes(baseName + Application.identifier);
372 using (var sha = SHA256.Create())
373 {
374 byte[] hash = sha.ComputeHash(nameBytes);
375 string b64 = Convert.ToBase64String(hash);
376 string safe = b64.Replace('+', '-').Replace('/', '_').Replace('=', 'x');
377 ClearBytes(hash);
378 return safe.Substring(0, Math.Min(28, safe.Length)) + ".dat";
379 }
380 }
381
382 private bool IsTamperedOrDebug()
383 {
384 if (!EnableHardening) return false;
385 try
386 {
387 if (EnableAntiDebugChecks)
388 {
389 if (Debug.isDebugBuild) return true;
390 if (Debugger.IsAttached) return true;
391 }
392 }
393 catch { }
394 try
395 {
396 if (EnableRootDetection)
397 {
398 string[] suspectPaths = new string[] { "/system/bin/su", "/system/xbin/su", "/sbin/su" };
399 foreach (var p in suspectPaths) if (File.Exists(p)) return true;
400 }
401 }
402 catch { }
403 return false;
404 }
r183
405} }}}
r185
406=== 실행 코드 ===
r183
407{{{#!syntax csharp
408using UnityEngine;
409
r185
410/// <summary>
411/// SecureManager의 Save 및 Load 메서드를 시연하는 예시 스크립트입니다.
412/// </summary>
r183
413public class ExampleUsage : MonoBehaviour
414{
415 private readonly string testPlayerName = "Gemini_Test_User";
416 private readonly int initialScore = 1000;
417 private readonly int newScore = 5000;
418
419 void Start()
420 {
r185
421 // SecureManager가 씬에 존재하는지 확인합니다.
r183
422 if (SecureManager.Instance == null)
423 {
424 Debug.LogError("SecureManager 인스턴스를 찾을 수 없습니다. SecureManager.cs를 GameObject에 추가했는지 확인해주세요.");
425 return;
426 }
r185
427
428 // 1. 데이터 저장 테스트
r183
429 SaveTestData();
430
r185
431 // 2. 데이터 로드 테스트
r183
432 LoadTestData();
433
r185
434 // 3. 데이터 업데이트 및 재저장
r183
435 UpdateAndSaveTestData();
r185
436
437 // 4. 업데이트된 데이터 로드 확인
r183
438 LoadTestData();
439 }
440
441 void SaveTestData()
442 {
443 Debug.Log("--- 1. 초기 데이터 저장 시도 ---");
444 PlayerData dataToSave = new PlayerData
445 {
446 playerName = testPlayerName,
447 score = initialScore
448 };
449
450 SecureManager.Instance.Save(dataToSave);
451 Debug.Log($"초기 데이터 저장 완료: Player={dataToSave.playerName}, Score={dataToSave.score}");
452 }
453
454 void LoadTestData()
455 {
456 Debug.Log("--- 2. 데이터 로드 시도 ---");
457 PlayerData loadedData = SecureManager.Instance.Load<PlayerData>();
458
459 if (loadedData != null)
460 {
461 Debug.Log($"데이터 로드 성공: Player={loadedData.playerName}, Score={loadedData.score}");
462 }
463 else
464 {
465 Debug.LogWarning("데이터 로드 실패 (파일 없음, 오류 발생, 혹은 디버거 감지).");
466 }
467 }
468
469 void UpdateAndSaveTestData()
470 {
471 Debug.Log("--- 3. 데이터 업데이트 및 재저장 시도 ---");
472 PlayerData loadedData = SecureManager.Instance.Load<PlayerData>();
473
474 if (loadedData != null)
475 {
476 loadedData.score = newScore;
477 SecureManager.Instance.Save(loadedData);
478 Debug.Log($"데이터 업데이트 및 저장 완료: New Score={loadedData.score}");
479 }
480 }
481
482 void OnDestroy()
483 {
r185
484 // 애플리케이션 종료 시 디버거 체크가 활성화된 경우 에러 방지
r183
485 if (SecureManager.Instance != null && SecureManager.Instance.EnableAntiDebugChecks)
486 {
r187
487 SecureManager.Instance.EnableAntiDebugChecks = false;
r183
488 Debug.Log("SecureManager: Anti-Debug Checks가 활성화되어 있습니다.");
489 }
490 }
r182
491} }}}