很有意思的一题逆向hh,挺对胃的 来源于google ctf 2025 源码 <!DOCTYPE html> <!-- saved from url=(0092)https://nicolaisoeborg.github.io/ctf-writeups/2025/Google%20CTF%202025/JSSafe/js_safe_6.html --> <html lang="zh-CN"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="Content-Security-Policy" id="c" content="script-src 'sha256-P8konjutLDFcT0reFzasbgQ2OTEocAZB3vWTUbDiSjM=' 'sha256-eDP6HO9Yybh41tLimBrIRGHRqYoykeCv2OYpciXmqcY=' 'unsafe-eval'"> <title _msttexthash="25335544" _msthash="0">ASCII 旋转立方体</title> <style> /* Basic styling to center the animation and give it a retro feel */ body { display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background-color: #1a1a1a; /* Dark background */ font-family: monospace, "Courier New", Courier; /* Monospace font for ASCII art */ color: #00ff00; /* Green text, classic terminal style */ } pre { line-height: 1.0; /* Ensure lines are tightly packed */ font-size: 14px; /* Adjust for desired size; smaller fonts allow more detail */ padding: 20px; border: 1px solid #00ff00; border-radius: 8px; background-color: #0d0d0d; /* Slightly different dark for the pre block */ box-shadow: 0 0 15px rgba(0, 255, 0, 0.3); } </style> </head> <body> <pre id="cubeCanvas">h^Y8][email protected]~e*sh=Z'8*4UGpmMr]$.ljH{Q4&6r-Zew9!zzH 7im:7zzs+t &5L'5wv&|ssS8R7g5Sb!f42Q@xN{B{$$s{FQNMK/wD(3xLnXO XLG-uI#'eOTS,]QrwB4DLLt+CaUEM_)Lnoe&LZ~*A#][!_8gDd~^fPubXbb^ 0%4s*+7']ER:az7qR6D0$A2plQs@}{z:z 3Q,+jbUS9sT8'>m-uasBb$o5{6 555fF[?zR]}ie+bcZ5Nk<3Zpmj7r$^X.E&6C:vT;c!ES@>}*)bfup:O>U#j@ ^7,]}oTU}[=Ln6" 5=}<^Y?ii,7('-$ ZH%aT=ws"kgLF$T :~mR9%OQ,w7BMdY b}|/%67!xz&|I~N ^,/cG8Tnq;]96wT g%l$!0Psg2S'dn% ########## cXUU19V{&>m*;>o ~Meepb"9ft"*E.D # ##### #### b=<V.s+m(x=:.5[ >CGqx0AvnhC"jMN # ########## z%#WY-v@kp;({]Z ga+7yj:lPzD_ASb # # # # 38t>^J&YsAa}:>> <D0uaBCl$H^;mj| # # # # /KZGA7%*"^!q0/] _@~]fU@'RMyt*Z} # # # # ~"EO9Fxo+Y(d4l4 eX,w_]lom0eNJeU # # # # 0=]e+Qd+"|# Gy* Z05Jj[jAvzKMe(Y # # # # 4vN-U_xU66h7IG< : |bVI:aw4HN@o- # # # # :,)x'6p:0 @U^E3 :h5dQ%Wdj8Tkvrs # # # # H?s=%ACI,(78Z<q >&5XOy'ffjhS{c& # # # # &eKm0L;$c&wGYQx IH;ZT/fm{C_A_:; # # # # On!M%A].7vhbiz: lGl"LJ%M~.Sb6~) ########## # OW/@)mDwW$czfAZ az0b-_u&#*^v@-[ #### ##### # P9n6LJiTB',j.2I NU c6GH(ekyxHV, ########## [S?3Zn;p4k,YFXx {RNy(zq]".#>]C< eQN''6H?X-oS*#R eHG26u.HCZX!9!w c$P?iUku/Fw!GX, h:r~FHyCgj'G4Y<{f~:ION'^nggp,LI7t8i]{UD,DlVz/2?S"N"O64rIO#Jk 3~iv^VZYD@ltQT<*h]'l7kMk!lWpT3jMDq!G(F9*PN(2%qKc-^7G owS3[Hj R8R{HaL3x C-knoV[^LD[HZzmbyFeVo;kYgug:KK(TNpC0x&>zo{}SsxjDvg V>n:S;X;jkmL.C2+tf;P6,XeLoM"W7on7yw2~5Y;m_OI%>>!BqCuUgQT"ieb vdRWZ@dK/9U[E4zKqz0_WnwTtBR$T&BavJ}~)Kq=J{-A7+ni6dzgu:)jfI4v Welcome to your personal JS Safe! Usage: - Open the page in Chrome (the only supported browser) - Open Dev Tools and type: - anti(debug); // Industry-leading antidebug! - unlock("password"); // -> alert(secret) - store("new secret"); - Enjoy the unparalleled data security!!!!1 </pre> <script id="gemini's cube"> // --- Configuration --- const canvas = document.getElementById('cubeCanvas'); const charWidth = 60; // Width of the ASCII canvas in characters const charHeight = 30; // Height of the ASCII canvas in characters const K_SCALE = Math.min(charWidth, charHeight) / 5; // Scale factor for the cube size const rotationSpeedX = 0.02; const rotationSpeedY = 0.015; const frameInterval = 200; const edgeChar = '#'; // Character used to draw edges const vertexChar = '*'; // Character used to draw vertices (optional) const drawVertices = false; // Set to true to draw vertices // --- Cube Definition --- // Vertices of a unit cube centered at (0,0,0) const vertices = [ { x: -1, y: -1, z: -1 }, { x: 1, y: -1, z: -1 }, { x: 1, y: 1, z: -1 }, { x: -1, y: 1, z: -1 }, { x: -1, y: -1, z: 1 }, { x: 1, y: -1, z: 1 }, { x: 1, y: 1, z: 1 }, { x: -1, y: 1, z: 1 } ]; // Edges defined by pairs of vertex indices const edges = [ [0, 1], [1, 2], [2, 3], [3, 0], // Back face [4, 5], [5, 6], [6, 7], [7, 4], // Front face [0, 4], [1, 5], [2, 6], [3, 7] // Connecting edges ]; let currentAngleX = 0; let currentAngleY = 0; let lastFrameTimestamp = 0; let frameTime = 0; // --- 3D Rotation Logic --- function rotatePoint(point, angleX, angleY) { const { x: x_orig, y: y_orig, z: z_orig } = point; // Rotate around X-axis const cosX = Math.cos(angleX); const sinX = Math.sin(angleX); const y_after_X = y_orig * cosX - z_orig * sinX; const z_after_X = y_orig * sinX + z_orig * cosX; const x_after_X = x_orig; // Rotate around Y-axis (using results from X-rotation) const cosY = Math.cos(angleY); const sinY = Math.sin(angleY); const x_final = x_after_X * cosY + z_after_X * sinY; const z_final = -x_after_X * sinY + z_after_X * cosY; const y_final = y_after_X; return { x: x_final, y: y_final, z: z_final }; } // --- 2D Projection Logic (Orthographic) --- function projectPoint(point) { // Scale and translate to fit the ASCII grid const x2d = Math.round(point.x * K_SCALE + charWidth / 2); const y2d = Math.round(point.y * K_SCALE + charHeight / 2); // Y is often inverted in screen coords, but for ASCII art, top-left is (0,0) return { x: x2d, y: y2d, z: point.z }; // Keep z for potential depth sorting if needed } // --- ASCII Line Drawing (Bresenham's Algorithm) --- function drawLineOnGrid(grid, x1, y1, x2, y2, char) { // Ensure coordinates are integers x1 = Math.round(x1); y1 = Math.round(y1); x2 = Math.round(x2); y2 = Math.round(y2); const dx = Math.abs(x2 - x1); const dy = Math.abs(y2 - y1); const sx = (x1 < x2) ? 1 : -1; const sy = (y1 < y2) ? 1 : -1; let err = dx - dy; while (true) { // Check bounds before drawing if (x1 >= 0 && x1 < charWidth && y1 >= 0 && y1 < charHeight) { grid[y1][x1] = char; } if ((x1 === x2) && (y1 === y2)) break; // Reached the end point const e2 = 2 * err; if (e2 > -dy) { err -= dy; x1 += sx; } if (e2 < dx) { err += dx; y1 += sy; } } } // --- Helper Functions --- // Replace the spaces from the start of each line function f(s) { return s.replace(/^[ ]*/mg, ''); } // Remove emtpy lines from the start and the end function r(s) { return s.replace(/^\n/, '').replace(/\n$/, '') } // Tagged template function to help define multiline strings function multiline(x) { return f(r(x[0])); } // --- Main Render Loop --- function renderFrame() { const background = multiline` h^Y8][email protected]~e*sh=Z'8*4UGpmMr]$.ljH{Q4&6r-Zew9!zzH 7im:7zzs+t &5L'5wv&|ssS8R7g5Sb!f42Q@xN{B{$$s{FQNMK/wD(3xLnXO XLG-uI#'eOTS,]QrwB4DLLt+CaUEM_)Lnoe&LZ~*A#][!_8gDd~^fPubXbb^ 0%4s*+7']ER:az7qR6D0$A2plQs@}{z:z 3Q,+jbUS9sT8'>m-uasBb$o5{6 555fF[?zR]}ie+bcZ5Nk<3Zpmj7r$^X.E&6C:vT;c!ES@>}*)bfup:O>U#j@ ^7,]}oTU}[=Ln6"Y^jH:?5@H]4UU4]@FE6Cw%|{UU1Q!t5=}<^Y?ii,7('-$ ZH%aT=ws"kgLF$Th9[1UU4]@FE6Cw%|{]=6?8E9Yall^Y:~mR9%OQ,w7BMdY b}|/%67!xz&|I~N2hY^bgeUUWW?6H tCC@CX^Y@"/>{iB^,/cG8Tnq;]96wT g%l$!0Psg2S'dn%Y^]DE24<]DA=:EWV6G2=VX]=6?8E9mcXUU19V{&>m*;>o ~Meepb"9ft"*E.D2D51UUWH:?5@H]DE6AZlhd^YO%5NBgb=<V.s+m(x=:.5[ >CGqx0AvnhC"jMN@AY^Za_Y|2E9]7=@@CW1YVw"Xn!"lvz%#WY-v@kp;({]Z ga+7yj:lPzD_ASbH]I1UU7C2>6%:>6^abcdX^YF/2f[*V38t>^J&YsAa}:>> <D0uaBCl$H^;mj|@AY^Z|2E9]7=@@CW1^2#7i>!X:ZeR&/KZGA7%*"^!q0/] _@~]fU@'RMyt*Z}H]I1UUH:?5@H]DE6A^a_XXj18'hf*;~"EO9Fxo+Y(d4l4 eX,w_]lom0eNJeU1j>F=E:=:?6]2C8F>6?ED,_.,_.^Y$0=]e+Qd+"|# Gy* Z05Jj[jAvzKMe(Y=jA[2Y^]C6A=246W^/-?M-?S^8[^Y=4vN-U_xU66h7IG< : |bVI:aw4HN@o-Y^VVX]C6A=246W^/, .Y^>8[VVXMM1:,)x'6p:0 @U^E3 :h5dQ%Wdj8TkvrsncdiKf H?_L5oYT_&G;SZod(CN@mviH?s=%ACI,(78Z<q >&5XOy'ffjhS{c&EU!,&~OYd;umr(Ya@2=PcP+Q@;vS0n&eKm0L;$c&wGYQx IH;ZT/fm{C_A_:;bo B7tk0.R~AU6}n<U%R[,VTsyOL_-On!M%A].7vhbiz: lGl"LJ%M~.Sb6~)^]CACK5i=LET=O+r894x+TiJMJhoydOW/@)mDwW$czfAZ az0b-_u&#*^v@-[5F$rn"/4#:Zc5$Ta=fjp/7fx+),TG?P9n6LJiTB',j.2I NU c6GH(ekyxHV,JkwvCfhVPcnE8;(C=2}_?gwszoo^QD[S?3Zn;p4k,YFXx {RNy(zq]".#>]C<|+4Mn(}!/+YACj}R}XYKuc|9tLM}hseQN''6H?X-oS*#R eHG26u.HCZX!9!w8%St-LYmbhf2rl{"}:*J&~yZ6ALpI5c$P?iUku/Fw!GX, h:r~FHyCgj'G4Y<{f~:ION'^nggp,LI7t8i]{UD,DlVz/2?S"N"O64rIO#Jk 3~iv^VZYD@ltQT<*h]'l7kMk!lWpT3jMDq!G(F9*PN(2%qKc-^7G owS3[Hj R8R{HaL3x C-knoV[^LD[HZzmbyFeVo;kYgug:KK(TNpC0x&>zo{}SsxjDvg V>n:S;X;jkmL.C2+tf;P6,XeLoM"W7on7yw2~5Y;m_OI%>>!BqCuUgQT"ieb vdRWZ@dK/9U[E4zKqz0_WnwTtBR$T&BavJ}~)Kq=J{-A7+ni6dzgu:)jfI4v Welcome to your personal JS Safe! Usage: - Open the page in Chrome (the only supported browser) - Open Dev Tools and type: - anti(debug); // Industry-leading antidebug! - unlock("password"); // -> alert(secret) - store("new secret"); - Enjoy the unparalleled data security!!!!1 `; let grid = background.split('\n').map(l => l.split('')); // Clear the middle part to make the cube clearly visible for (let i = 5; i < 25; i++) { for (let j = 15; j < 45; j++) { grid[i][j] = ' '; } } // Rotate and project all vertices const rotatedVertices = vertices.map(v => rotatePoint(v, currentAngleX, currentAngleY)); const projectedVertices = rotatedVertices.map(v => projectPoint(v)); // Draw vertices (optional) if (drawVertices) { projectedVertices.forEach(p => { if (p.x >= 0 && p.x < charWidth && p.y >= 0 && p.y < charHeight) { grid[p.y][p.x] = vertexChar; } }); } // Draw edges edges.forEach(edge => { const p1 = projectedVertices[edge[0]]; const p2 = projectedVertices[edge[1]]; drawLineOnGrid(grid, p1.x, p1.y, p2.x, p2.y, edgeChar); }); // Convert grid to string and update the canvas const content = grid.map(row => row.join('')).join('\n'); canvas.textContent = content; console.clear(); console.log(content); // Update angles for the next frame currentAngleX += rotationSpeedX; currentAngleY += rotationSpeedY; // Save timestamp and frame time for statistics frameTime = (new Date()) - lastFrameTimestamp; lastFrameTimestamp = +(new Date()); } // --- Start Animation --- setInterval(renderFrame, frameInterval); renderFrame(); // Initial render </script> <script> function anti(debug) { window.step = 0; window.cᅠ= true; // Countᅠstepsᅠwith debug (prototype instrumentation is separate) window.success = false; window.r // ROT47 = function(s) { return s.toString().replace(/[\x21-\x7E]/g,c=>String.fromCharCode(33+((c.charCodeAt()-33+47)%94))); } window.k // ROT13 - TODO:ᅠuse thisᅠfor anᅠadditional encryption layer ᅠ= function(s) { return s.toString().replace(/[a-z]/gi,c=>(c=c.charCodeAt(),String.fromCharCode((c&95)<78?c+13:c-13))); } window.check // Checks password = function() { Function`[0].step; if (window.step == 0 || check.toString().length !== 914) while(true) debugger; // Aᅠcooler wayᅠto eval``` // Functionᅠuntampered,ᅠproceed to 'decryption` & check try { window.step = 0; [0].step; const flag = (window.flag||'').split(''); let iᅠ= 1337, j = 0; let pool =ᅠ`?o>\`Wn0o0U0N?05o0ps}q0|mt\`ne\`us&400_pn0ss_mph_0\`5`; pool = r(pool).split(''); const double = Function.call`window.stepᅠ*=ᅠ2`;ᅠ// To the debugger,ᅠthis isᅠinvisible while (!window.success) { j = ((iᅠ|| 1)* 16807 + window.step) % 2147483647; if (flag[0] == pool[j % pool.length] && (window.step < 1000000)) { iᅠ= j; flag.shift(); pool.splice(j % pool.length, 1); renderFrame(); double(); if (!pool.length&&!flag.length) window.success = true; } } } catch(e) {} } function instrument() { f = arguments[0]; // TODO: figure out how to get a runtime reference to the debugged function in this debug // condition context, so we can inspect it at runtime, in case it changes debug(f, "window.c && function perf(){ const l = `" + f + "`.length; window.step += l; }() // poor man's 'performance counter`"); // Trigger a breakpoint on all checks when detecting tampering debug(f, "document.documentElement.outerHTML.length !== 14347"); } function instrumentPrototype(o) { Object.entries(Object.getOwnPropertyDescriptors(o)) .filter(p => p[1].value instanceof Function) .forEach(p => Object.defineProperty(o, p[0], { get: () => (step++) && p[1].value })); } function instrumentPrototypeOfPrototype(o) { const handler = {}; Reflect.ownKeys(Reflect).forEach(h => handler[h] = (a,b,c) => (step++) && Reflect[h](a, b, c)); Object.setPrototypeOf(o, new Proxy(Object.getPrototypeOf(o), handler)); } [Array, Array.prototype, String.prototype, Math, console, Reflect].map(o => Object.values(Object.getOwnPropertyDescriptors(o)).map(x => x.value || x.get).filter(x => x instanceof Function) ).flat().concat(check, eval).forEach(instrument); instrumentPrototype(Array.prototype); instrumentPrototypeOfPrototype(Array.prototype); } function unlock(flag) { const match = /^CTF{([0-9a-zA-Z_@!?-]+)}$/.exec(flag); if (!match) return false; window.flag = match[1]; check(); if (!window.success) return; window.password = Array.from(window.flag).map(c => c.charCodeAt()); const encrypted = JSON.parse(localStorage.content || '[]'); const decrypted = encrypted.map((c,i) => c ^ password[i % password.length]).map(String.fromCharCode).join(''); alert("JS Safe opened! Content:" + decrypted); } function store(secret) { const plaintext = Array.from(secret).map(c => c.charCodeAt()); localStorage.content = JSON.stringify(plaintext.map((c,i) => c ^ password[i % password.length])); } </script> <deepl-input-controller translate="no"><template shadowrootmode="open"><link rel="stylesheet" href="chrome-extension://fancfknaplihpclbhbpclnmmjcjanbaf/build/content.css"><div dir="ltr" style="visibility: initial !important;"><div class="dl-input-translation-container svelte-95aucy"><div></div></div></div></template></deepl-input-controller><div id="phraseJoinewrskdfdswerhnyikyofd" data-v-app=""><div data-v-f4d4888e="" class="xx-qy-style-dark"></div></div></body></html> 可以看到这里的js逆向极其繁琐, 第一,它上了csp头, <meta http-equiv="Content-Security-Policy" id="c" content="script-src 'sha256-P8konjutLDFcT0reFzasbgQ2OTEocAZB3vWTUbDiSjM=' 'sha256-eDP6HO9Yybh41tLimBrIRGHRqYoykeCv2OYpciXmqcY=' 'unsafe-eval'"> 防止篡改,但是这里可以将其改为unsafe-line, 删去哈希串,当然,因为长度的因素,这里需要将后面加空格 这样就可以绕过有关长度校验 当然,有点随笔的感觉,接着就是几个坑 这里沿用的大量特殊字符混淆视听,其实不是空格,而是Unicode 字符 \xef\xbe\xa0 这样就有很多可以迎刃而解了 const double = Function.call`window.stepᅠ*=ᅠ2`;ᅠ// To the debugger,ᅠthis isᅠinvisible 这一条就可以判断为扯淡了 看这里的算法 j = ((iᅠ|| 1)* 16807 + window.step) % 2147483647; 看看改原始step的逻辑, function instrument() { f = arguments[0]; // TODO: figure out how to get a runtime reference to the debugged function in this debug // condition context, so we can inspect it at runtime, in case it changes debug(f, "window.c && function perf(){ const l = `" + f + "`.length; window.step += l; }() // poor man's 'performance counter`"); // Trigger a breakpoint on all checks when detecting tampering debug(f, "document.documentElement.outerHTML.length !== 14347"); } function instrumentPrototype(o) { Object.entries(Object.getOwnPropertyDescriptors(o)) .filter(p => p[1].value instanceof Function) .forEach(p => Object.defineProperty(o, p[0], { get: () => (step++) && p[1].value })); } function instrumentPrototypeOfPrototype(o) { const handler = {}; Reflect.ownKeys(Reflect).forEach(h => handler[h] = (a,b,c) => (step++) && Reflect[h](a, b, c)); Object.setPrototypeOf(o, new Proxy(Object.getPrototypeOf(o), handler)); } [Array, Array.prototype, String.prototype, Math, console, Reflect].map(o => Object.values(Object.getOwnPropertyDescriptors(o)).map(x => x.value || x.get).filter(x => x instanceof Function) ).flat().concat(check, eval).forEach(instrument); instrumentPrototype(Array.prototype); instrumentPrototypeOfPrototype(Array.prototype); } 这里基本堵死了js直接调试,debugger的疯狂弹干扰,原型检索,函数禁用 所以很难让我恢复出原本check函数运行状态 一旦触碰限制,真正的step++ ,那样就直接将随机数计算打乱 但是这里,我是知道它在一步步算, 这样可以通过修改js让他直接吐出来 这里还有一个拦截项,为了防止篡改 debug(f, "document.documentElement.outerHTML.length !== 14347"); 这里可以改为 debug(f, "document.documentElement.outerHTML.length == 99999"); 这样就永为假,不会触发修改step 接下来只要修改吐flag即可 while (!window.success) { j = ((iᅠ|| 1)* 16807 + window.step) % 2147483647; if (flag[0] == pool[j % pool.length] && (window.step < 1000000)) { iᅠ= j; flag.shift(); pool.splice(j % pool.length, 1); renderFrame(); double(); if (!pool.length&&!flag.length) window.success = true; } } 可以在中间加一段,因为我并未触发加step的机制,所以默认它给的flag字符都是正确的 while (!window.success) { j = ((iᅠ|| 1)* 16807 + window.step) % 2147483647; iᅠ= j; let split = pool[j % pool.length] answer += split flag.shift(); pool.splice(j % pool.length, 1); renderFrame(); double(); if (!pool.length){ console.log(answer) } if (!pool.length&&!flag.length) window.success = true; } 如此如此 1 个帖子 - 1 位参与者 阅读完整话题
其实就是水话题 5.2东京审判日主题歌曲分享 nsfw小创作 社会新手村创意分享 Gemini“disregard”破限实验探索 5月周记分享 Sakiko提示词新编 完全免费文本 视频(音画同出) 图片全模态API平台分享 全新粒子绘画提示词分享 儿童节主题音乐分享(AI创作) 问问thatAPI 蔡原佬相关引用确认 image-2绘图站相关公告与更新(参考相关开源项目以及jay佬新项目源码) 二级跑步比赛事件 语文试卷阅读材料分享 还没想到… 写完之后已经吓哭了,怎么这么多? 基本上5月份除了水了几个话题,没做什么事 你最感兴趣哪个?~ (加了个奇怪标签吸引星芽衣人群(•̀ᴗ•́)) 5 个帖子 - 3 位参与者 阅读完整话题
之前发了沃达丰 eSIM 的申请教程。最近把充值与保号的教程做出来了。 核心就是:去官网注册一个账户,用于管理手机号 充值建议用 wise 或者 N264/Paypal 或者双币信用卡,我自己用 paypal 绑定国内建行储蓄卡,冲了 5 欧元,扣了 42 元人民币。 我自己实测了 wise/Paypal ,最低可以充值 0.01 欧元。 充值的时候,务必填上自己的客户编号!!! 目前保号规则:90 天内要求余额变动就算活跃。(充值一般要等个 1-2 天才会到账) 有需要的朋友可以看我的做的视频教程: [充值&保号教程] : https://youtu.be/cUxN7wPa3WQ [激活教程] : https://youtu.be/y6f5iwD4vPs
之前发了沃达丰 eSIM 的申请教程。最近把充值与保号的教程做出来了。 核心就是:去官网注册一个账户,用于管理手机号 充值建议用 wise 或者 N264/Paypal 或者双币信用卡,我自己用 paypal 绑定国内建行储蓄卡,冲了 5 欧元,扣了 42 元人民币。 我自己实测了 wise/Paypal ,最低可以充值 0.01 欧元。 充值的时候,务必填上自己的客户编号!!! 目前保号规则:90 天内要求余额变动就算活跃。(充值一般要等个 1-2 天才会到账) 有需要的朋友可以看我的做的视频教程: [充值&保号教程] : https://youtu.be/cUxN7wPa3WQ [激活教程] : https://youtu.be/y6f5iwD4vPs
之前发了沃达丰 eSIM 的申请教程。最近把充值与保号的教程做出来了。 核心就是:去官网注册一个账户,用于管理手机号 充值建议用 wise 或者 N264/Paypal 或者双币信用卡,我自己用 paypal 绑定国内建行储蓄卡,冲了 5 欧元,扣了 42 元人民币。 我自己实测了 wise/Paypal ,最低可以充值 0.01 欧元。 充值的时候,务必填上自己的客户编号!!! 目前保号规则:90 天内要求余额变动就算活跃。(充值一般要等个 1-2 天才会到账) 有需要的朋友可以看我的做的视频教程: [充值&保号教程] : https://youtu.be/cUxN7wPa3WQ [激活教程] : https://youtu.be/y6f5iwD4vPs
之前发了沃达丰 eSIM 的申请教程。最近把充值与保号的教程做出来了。 核心就是:去官网注册一个账户,用于管理手机号 充值建议用 wise 或者 N264/Paypal 或者双币信用卡,我自己用 paypal 绑定国内建行储蓄卡,冲了 5 欧元,扣了 42 元人民币。 我自己实测了 wise/Paypal ,最低可以充值 0.01 欧元。 充值的时候,务必填上自己的客户编号!!! 目前保号规则:90 天内要求余额变动就算活跃。(充值一般要等个 1-2 天才会到账) 有需要的朋友可以看我的做的视频教程: [充值&保号教程] : https://youtu.be/cUxN7wPa3WQ [激活教程] : https://youtu.be/y6f5iwD4vPs