code wiki / (root) / nx_lib_pmc_pow.nx

nx_lib_pmc_pow.nx source

↩ module page · 155 lines · 7027 B

1// nx_lib_pmc_pow.nx -- solve NCBI PMC's JS proof-of-work anti-bot gate, all 2// sovereign. One run: fetch the interstitial over TLS-1.3 -> extract the FRESH 3// POW_CHALLENGE + ncbi_sid -> hashcash solve (SHA-256(challenge+nonce) hex must 4// start with "0000" = first two digest bytes zero; the exact algorithm from 5// NCBI's pow.js) -> refetch with cookie cloudpmc-viewer-pow=<challenge>,<nonce> 6// -> the real PDF -> extract its text. No browser, no JS engine, no python. 7import "nx_syscalls.nx" 8import "nx_lib_fetch.nx" 9import "nx_lib_fetch_cookie.nx" 10import "nx_sha256_wasm.nx" 11import "nx_lib_pdf.nx" 12const PMC_MAGIC_16777216: i64 = 16777216 13const PMC_MAGIC_1024: i64 = 1024 14const PMC_MAGIC_2048: i64 = 2048 15const PMC_MAGIC_50000000: i64 = 50000000 16 17func pw_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 18func pw_wr(p: *u8, n: i64) -> i64 { sys_write(1, p, n); return 0 } 19func pw_putn(v: i64) -> i64 { 20 let bb: *u8 = sys_mmap(28); var m: i64 = v 21 if m < 0 { m = 0 - m; sys_write(1, "-" as *u8, 1) } 22 let t: *u8 = sys_mmap(28); var k: i64 = 0 23 if m == 0 { t[0] = 48 as u8; k = 1 } 24 while m > 0 { t[k] = (48 + (m - (m/10)*10)) as u8; m = m / 10; k = k + 1 } 25 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } sys_write(1, bb, k); return 0 26} 27 28// index just past the first occurrence of needle in hay, or -1. 29func pw_find_after(hay: *u8, hayn: i64, needle: *u8) -> i64 { 30 var nlen: i64 = 0; while needle[nlen] != (0 as u8) { nlen = nlen + 1 } 31 if nlen == 0 { return 0 - 1 } 32 if hayn < nlen { return 0 - 1 } 33 let last: i64 = hayn - nlen 34 var i: i64 = 0 35 while i <= last { 36 var j: i64 = 0; var st: i64 = 0 37 while st == 0 { if j >= nlen { st = 2 } if st == 0 { if hay[i+j] != needle[j] { st = 1 } if st == 0 { j = j + 1 } } } 38 if st == 2 { return i + nlen } 39 i = i + 1 40 } 41 return 0 - 1 42} 43 44// copy hay[start..] into out until delim byte (or maxlen); return length. 45func pw_copy_until(hay: *u8, hayn: i64, start: i64, delim: i64, out: *u8, maxlen: i64) -> i64 { 46 var k: i64 = 0; var i: i64 = start; var go: i64 = 1 47 while go == 1 { 48 if i >= hayn { go = 0 } 49 else { if k >= maxlen { go = 0 } 50 else { if (hay[i] as i64) == delim { go = 0 } 51 else { out[k] = hay[i]; k = k + 1; i = i + 1 } } } 52 } 53 return k 54} 55 56// append decimal(v) at buf[off]; return number of digits written. 57func pw_dec_into(buf: *u8, off: i64, v: i64) -> i64 { 58 if v == 0 { buf[off] = 48 as u8; return 1 } 59 let tmp: *u8 = sys_mmap(32); var k: i64 = 0; var x: i64 = v 60 while x > 0 { tmp[k] = (48 + (x - (x/10)*10)) as u8; x = x / 10; k = k + 1 } 61 var j: i64 = 0; while j < k { buf[off+j] = tmp[k-1-j]; j = j + 1 } 62 return k 63} 64 65func pw_str_into(buf: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { buf[off+i] = s[i]; i = i + 1 } return off + i } 66 67func pw_body_start(buf: *u8, n: i64) -> i64 { 68 var bi: i64 = 0 69 while bi + 3 < n { 70 if buf[bi] == 13 as u8 { if buf[bi+1] == 10 as u8 { if buf[bi+2] == 13 as u8 { if buf[bi+3] == 10 as u8 { return bi + 4 } } } } 71 bi = bi + 1 72 } 73 return 0 - 1 74} 75 76const PMC_URL: *u8 = "https://pmc.ncbi.nlm.nih.gov/articles/PMC7197042/pdf/CD004504.pdf\x00" 77 78func main() -> i64 { 79 let store: *TrustStore = nx_lib_fetch_store() 80 if store == 0 as *TrustStore { pw_puts("no CA store\n"); return 1 } 81 let cap: i64 = PMC_MAGIC_16777216 82 let buf: *u8 = sys_mmap(cap) 83 84 // ---- fetch #1: the PoW interstitial ---- 85 let g1: i64 = nx_lib_fetch(store, PMC_URL, buf, cap) 86 pw_puts("interstitial bytes="); pw_putn(g1); pw_puts("\n") 87 if g1 < 0 { return 2 } 88 89 let cstart: i64 = pw_find_after(buf, g1, "POW_CHALLENGE = \"" as *u8) 90 if cstart < 0 { pw_puts("no POW_CHALLENGE in response (format changed or not gated)\n"); return 3 } 91 let challenge: *u8 = sys_mmap(PMC_MAGIC_1024) 92 let chlen: i64 = pw_copy_until(buf, g1, cstart, 0x22, challenge, PMC_MAGIC_1024) 93 pw_puts("challenge=("); pw_putn(chlen); pw_puts("B) "); pw_wr(challenge, chlen); pw_puts("\n") 94 95 let sid: *u8 = sys_mmap(256) 96 var sidlen: i64 = 0 97 let sstart: i64 = pw_find_after(buf, g1, "ncbi_sid=" as *u8) 98 if sstart >= 0 { sidlen = pw_copy_until(buf, g1, sstart, 0x3B, sid, 256) } 99 100 // ---- solve: SHA-256(challenge + decimal(nonce)) hex must start "0000" ---- 101 let ctx: *u8 = sys_mmap(512) 102 let dig: *u8 = sys_mmap(64) 103 let cbuf: *u8 = sys_mmap(PMC_MAGIC_2048) 104 var ci: i64 = 0; while ci < chlen { cbuf[ci] = challenge[ci]; ci = ci + 1 } 105 var nonce: i64 = 0 106 var found: i64 = 0 107 while found == 0 { 108 let dlen: i64 = pw_dec_into(cbuf, chlen, nonce) 109 nx_sha256_one_shot(cbuf, chlen + dlen, ctx, dig) 110 if dig[0] == 0 as u8 { if dig[1] == 0 as u8 { found = 1 } } 111 if found == 0 { nonce = nonce + 1 } 112 if nonce > PMC_MAGIC_50000000 { pw_puts("no solution under cap\n"); return 4 } 113 } 114 pw_puts("SOLVED nonce="); pw_putn(nonce); pw_puts("\n") 115 116 // ---- build cookie + refetch ---- 117 let cookie: *u8 = sys_mmap(PMC_MAGIC_2048) 118 var co: i64 = 0 119 co = pw_str_into(cookie, co, "cloudpmc-viewer-pow=" as *u8) 120 var i2: i64 = 0; while i2 < chlen { cookie[co] = challenge[i2]; co = co + 1; i2 = i2 + 1 } 121 cookie[co] = 0x2C as u8; co = co + 1 122 co = co + pw_dec_into(cookie, co, nonce) 123 if sidlen > 0 { co = pw_str_into(cookie, co, "; ncbi_sid=" as *u8); i2 = 0; while i2 < sidlen { cookie[co] = sid[i2]; co = co + 1; i2 = i2 + 1 } } 124 pw_puts("cookie=("); pw_putn(co); pw_puts("B)\n") 125 126 let buf2: *u8 = sys_mmap(cap) 127 let g2: i64 = nx_lib_fetch_cookie(store, PMC_URL, cookie, co, buf2, cap) 128 pw_puts("refetch bytes="); pw_putn(g2); pw_puts("\n") 129 if g2 < 0 { return 5 } 130 131 let bs: i64 = pw_body_start(buf2, g2) 132 var bp: *u8 = buf2 133 var bl: i64 = g2 134 if bs >= 0 { bp = ((buf2 as i64) + bs) as *u8; bl = g2 - bs } 135 pw_puts("body bytes="); pw_putn(bl); pw_puts(" first8="); var fb: i64 = 8; if bl < 8 { fb = bl } pw_wr(bp, fb); pw_puts("\n") 136 137 // is it the PDF? 138 if bl >= 5 { if bp[0] == 0x25 as u8 { if bp[1] == 0x50 as u8 { if bp[2] == 0x44 as u8 { if bp[3] == 0x46 as u8 { 139 pw_puts("*** PoW SOLVED -> got the real PDF! saving + extracting text ***\n") 140 let fd: i64 = sys_openat_wr("knowledge/fetched/pmc_cd004504.pdf\x00" as *u8, 0x1A4) 141 if fd > 0 { sys_write(fd, bp, bl); sys_close(fd) } 142 let outx: *u8 = sys_mmap(cap) 143 let tn: i64 = nx_lib_pdf_text(bp, bl, outx, cap) 144 pw_puts("extracted text bytes="); pw_putn(tn); pw_puts("\n--- body sample @40% ---\n") 145 var soff: i64 = (tn / 10) * 4 146 var tt: i64 = 700; if soff + tt > tn { tt = tn - soff } 147 if tt > 0 { pw_wr(((outx as i64) + soff) as *u8, tt) } 148 pw_puts("\n--- end ---\n") 149 return 0 150 } } } } } 151 152 pw_puts("refetch NOT a PDF -- first 300B of body:\n") 153 var pb: i64 = 300; if bl < 300 { pb = bl } pw_wr(bp, pb); pw_puts("\n") 154 return 0 155}