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}