[{"data":1,"prerenderedAt":963},["ShallowReactive",2],{"posts":3},[4],{"id":5,"title":6,"body":7,"date":955,"description":956,"extension":957,"meta":958,"navigation":784,"path":959,"seo":960,"stem":961,"__hash__":962},"posts\u002Fposts\u002F2026-07\u002Ffingerprinting-cpus.md","Fingerprinting CPUs Using CPU-Specific Behavior",{"type":8,"value":9,"toc":950},"minimark",[10,14,19,27,36,70,78,156,159,163,178,184,226,235,238,398,404,439,449,466,469,496,499,505,524,555,567,636,643,647,661,668,818,834,919,922,925,946],[11,12,13],"p",{},"The x86 architecture is arguably the most complex general-purpose Instruction Set Architecture ever made.\nIt has thousands of different instructions (depending on how you count).\nThe reference manuals from Intel and AMD are 5,000+ pages.\nThis complexity can be (ab)used to fingerprint CPUs.",[15,16,18],"h2",{"id":17},"undefined-behavior","Undefined Behavior",[11,20,21,22,26],{},"One of the most awful parts of x86, in my opinion, is that it is not fully defined:\nsome instructions or specific instruction outputs are ",[23,24,25],"em",{},"undefined",", which means they are allowed produce any outcome the CPU manufacturer sees fit.\nThe usual advice is not to rely on such behavior, as it may change in future CPUs.",[28,29,32,33],"div",{"className":30},[31],"sidenote","\n  \"Undefined behavior\" on CPUs is not the same as undefined behavior in C.\n  In C, the compiler is allowed to assume undefined behavior never occurs, and may decide to delete entire branches of your code (or worse) if it is able to determine that it contains undefined behavior.\n",[11,34,35],{},"On CPUs, \"undefined\" just means that the architecture makes no guarantee about the result,\nand different processors are allowed to produce different results.\nThis is closer to \"implementation-defined behavior\" in C, if you consider every CPU as a different implementation.",[11,37,38,39,42,43,47,48,69],{},"For many instructions with undefined behavior, different CPUs ",[23,40,41],{},"do"," actually behave differently.\nIt can differ between manufacturer (Intel vs. AMD),\nbut also between several different microarchitectures of the same manufacturer. For example, the ",[44,45,46],"code",{},"MUL"," instruction ",[49,50,54,55,58,59,58,62,65,66],"a",{"href":51,"rel":52},"https:\u002F\u002Fwww.felixcloutier.com\u002Fx86\u002Fmul",[53],"nofollow","has four undefined flags: ",[44,56,57],{},"PF",", ",[44,60,61],{},"AF",[44,63,64],{},"ZF"," and ",[44,67,68],{},"SF",".\nI can only speculate as to the reason for this, but I would guess that it is simply historical: on the 8086, correctly updating the flags would have cost an additional microcode operation and thus would have made all multiplications one cycle slower, which Intel likely did not consider worthwhile.",[11,71,72,77],{},[49,73,76],{"href":74,"rel":75},"https:\u002F\u002Fexplore.liblisa.nl\u002Finstruction\u002FF7E0",[53],"Analyzing this instruction on several different architectures",", shows us how it behaves differently:",[79,80,81,107],"table",{},[82,83,84],"thead",{},[85,86,87,91,98,104],"tr",{},[88,89,90],"th",{},"CPU",[88,92,93,94,97],{},"AMD 3900X",[95,96],"br",{},"AMD 7700X",[88,99,100,101,103],{},"Intel i9-13900 (P)",[95,102],{},"Xeon Silver 4110",[88,105,106],{},"Intel i9-13900 (E)",[108,109,110,123,134,145],"tbody",{},[85,111,112,115,118,121],{},[113,114,57],"td",{},[113,116,117],{},"unchanged",[113,119,120],{},"parity(x*x)",[113,122,120],{},[85,124,125,127,129,132],{},[113,126,61],{},[113,128,117],{},[113,130,131],{},"0",[113,133,131],{},[85,135,136,138,140,142],{},[113,137,64],{},[113,139,117],{},[113,141,131],{},[113,143,144],{},"(x*x == 0)",[85,146,147,149,151,154],{},[113,148,68],{},[113,150,117],{},[113,152,153],{},"sign(x*x)",[113,155,153],{},[11,157,158],{},"Note that for the Intel i9-13900, which has two different types of CPU cores (efficiency and performance cores),\nthe ZF actually behaves differently depending on which core your OS has scheduled your thread to run on!",[15,160,162],{"id":161},"building-a-fingerprinting-program","Building a fingerprinting program",[11,164,165,166,171,172,65,174,177],{},"We can (ab)use differences in CPU-implementations to determine which CPU our code is running on.\nWe will consider only the AMD 3900X, AMD 7700X, Intel i9-13900 (P or E) and Intel Xeon Silver 4110 CPUs, as these are the only CPUs that ",[49,167,170],{"href":168,"rel":169},"https:\u002F\u002Fexplore.liblisa.nl\u002F",[53],"have been analyzed by libLISA",".\nFor this, we need to find a set of instructions whose CPU-specific behavior allows us to uniquely identify which CPU our program is running on.\nUsing libLISA's analysis results, we can find two such instructions that allow us to do this: ",[44,173,46],{},[44,175,176],{},"VPCLMULHQLQDQ",".",[11,179,180,181,183],{},"First, the ",[44,182,46],{}," instruction allows us to distinguish between three groups of architectures.\nTo ensure we actually do get three different results, we need to pick specific inputs.\nUsing the multiplication 0 * 1 with ZF=0 and PF=0 is one possible input that works.\nIt gives us one of these results:",[79,185,186,203],{},[82,187,188],{},[85,189,190,193,197,201],{},[88,191,192],{},"Flag",[88,194,93,195,97],{},[95,196],{},[88,198,100,199,103],{},[95,200],{},[88,202,106],{},[108,204,205,216],{},[85,206,207,209,211,214],{},[113,208,57],{},[113,210,131],{},[113,212,213],{},"1",[113,215,213],{},[85,217,218,220,222,224],{},[113,219,64],{},[113,221,131],{},[113,223,131],{},[113,225,213],{},[11,227,228,229,231,232,234],{},"Now we just need to find an instruction that allows us to distinguish between the AMD 3900X and the AMD 7700X, or the Intel i9-13900 (P) and the Xeon Silver 4110.\nAs it turns out, such an instruction exists: ",[44,230,176],{}," (I recommend the following pronunciation: V-P-C-L-MUL-HQ-LQ-DQ).\nAlthough ",[44,233,176],{}," doesn't have any undefined behavior,\nit is not supported on the AMD 3900X and the Xeon Silver 4110.\nThis means we can use its (non)-existence to distinguish between the two CPUs within each group.",[11,236,237],{},"Putting it all together, our full code becomes:",[239,240,245],"pre",{"className":241,"code":242,"language":243,"meta":244,"style":244},"language-asm shiki shiki-themes github-light github-dark","  xor rdx, rdx\n  xor rax, rax\n  add dl, 1\n  mul eax\n  setp r13b\n  setz r14b\n  lea rdi, [rip + 2f]\n  xor r15b, r15b\n  vpclmulhqlqdq ymm0,ymm1,ymm2\n  mov r15b, 1\n2:\n  ...\n","asm","",[44,246,247,266,279,293,302,311,320,338,351,371,383,392],{"__ignoreMap":244},[248,249,252,256,260,263],"span",{"class":250,"line":251},"line",1,[248,253,255],{"class":254},"szBVR","  xor",[248,257,259],{"class":258},"sj4cs"," rdx",[248,261,58],{"class":262},"sVt8B",[248,264,265],{"class":258},"rdx\n",[248,267,269,271,274,276],{"class":250,"line":268},2,[248,270,255],{"class":254},[248,272,273],{"class":258}," rax",[248,275,58],{"class":262},[248,277,278],{"class":258},"rax\n",[248,280,282,285,288,290],{"class":250,"line":281},3,[248,283,284],{"class":254},"  add",[248,286,287],{"class":258}," dl",[248,289,58],{"class":262},[248,291,292],{"class":258},"1\n",[248,294,296,299],{"class":250,"line":295},4,[248,297,298],{"class":254},"  mul",[248,300,301],{"class":258}," eax\n",[248,303,305,308],{"class":250,"line":304},5,[248,306,307],{"class":254},"  setp",[248,309,310],{"class":258}," r13b\n",[248,312,314,317],{"class":250,"line":313},6,[248,315,316],{"class":254},"  setz",[248,318,319],{"class":258}," r14b\n",[248,321,323,326,329,332,335],{"class":250,"line":322},7,[248,324,325],{"class":254},"  lea",[248,327,328],{"class":258}," rdi",[248,330,331],{"class":262},", [",[248,333,334],{"class":258},"rip",[248,336,337],{"class":262}," + 2f]\n",[248,339,341,343,346,348],{"class":250,"line":340},8,[248,342,255],{"class":254},[248,344,345],{"class":258}," r15b",[248,347,58],{"class":262},[248,349,350],{"class":258},"r15b\n",[248,352,354,357,360,363,366,368],{"class":250,"line":353},9,[248,355,356],{"class":254},"  vpclmulhqlqdq",[248,358,359],{"class":258}," ymm0",[248,361,362],{"class":262},",",[248,364,365],{"class":258},"ymm1",[248,367,362],{"class":262},[248,369,370],{"class":258},"ymm2\n",[248,372,374,377,379,381],{"class":250,"line":373},10,[248,375,376],{"class":254},"  mov",[248,378,345],{"class":258},[248,380,58],{"class":262},[248,382,292],{"class":258},[248,384,386,389],{"class":250,"line":385},11,[248,387,388],{"class":258},"2",[248,390,391],{"class":262},":\n",[248,393,395],{"class":250,"line":394},12,[248,396,397],{"class":262},"  ...\n",[11,399,400,401,403],{},"It looks more complicated than it is.\nThe first three instructions set up the correct inputs for the ",[44,402,46],{}," instruction.\nThey result in a CPU state where RAX = 0, RDX = 1, ZF=0 and PF=0:",[239,405,407],{"className":241,"code":406,"language":243,"meta":244,"style":244},"  xor rdx, rdx\n  xor rax, rax\n  add dl, 1\n",[44,408,409,419,429],{"__ignoreMap":244},[248,410,411,413,415,417],{"class":250,"line":251},[248,412,255],{"class":254},[248,414,259],{"class":258},[248,416,58],{"class":262},[248,418,265],{"class":258},[248,420,421,423,425,427],{"class":250,"line":268},[248,422,255],{"class":254},[248,424,273],{"class":258},[248,426,58],{"class":262},[248,428,278],{"class":258},[248,430,431,433,435,437],{"class":250,"line":281},[248,432,284],{"class":254},[248,434,287],{"class":258},[248,436,58],{"class":262},[248,438,292],{"class":258},[11,440,441,442,65,445,448],{},"Then, ",[44,443,444],{},"SETP",[44,446,447],{},"SETZ"," store the outputs of the PF and ZF flag in registers:",[239,450,452],{"className":241,"code":451,"language":243,"meta":244,"style":244},"  setp r13b\n  setz r14b\n",[44,453,454,460],{"__ignoreMap":244},[248,455,456,458],{"class":250,"line":251},[248,457,307],{"class":254},[248,459,310],{"class":258},[248,461,462,464],{"class":250,"line":268},[248,463,316],{"class":254},[248,465,319],{"class":258},[11,467,468],{},"For the second observation, we start by loading a jump offset (label '2') into the RDI register and zeroing R15:",[239,470,472],{"className":241,"code":471,"language":243,"meta":244,"style":244},"  lea rdi, [rip + 2f]\n  xor r15b, r15b\n",[44,473,474,486],{"__ignoreMap":244},[248,475,476,478,480,482,484],{"class":250,"line":251},[248,477,325],{"class":254},[248,479,328],{"class":258},[248,481,331],{"class":262},[248,483,334],{"class":258},[248,485,337],{"class":262},[248,487,488,490,492,494],{"class":250,"line":268},[248,489,255],{"class":254},[248,491,345],{"class":258},[248,493,58],{"class":262},[248,495,350],{"class":258},[11,497,498],{},"If we jump to this offset, the remaining instructions are skipped.",[11,500,501,502,504],{},"Finally, we execute the ",[44,503,176],{}," instruction:",[239,506,508],{"className":241,"code":507,"language":243,"meta":244,"style":244},"  vpclmulhqlqdq ymm0,ymm1,ymm2\n",[44,509,510],{"__ignoreMap":244},[248,511,512,514,516,518,520,522],{"class":250,"line":251},[248,513,356],{"class":254},[248,515,359],{"class":258},[248,517,362],{"class":262},[248,519,365],{"class":258},[248,521,362],{"class":262},[248,523,370],{"class":258},[11,525,526,527,530,531,534,535,538,539,541,542,545,546,548,549,551,552,554],{},"If the instruction does not exist, an ",[44,528,529],{},"#UD"," exception is raised.\nWe can catch this exception with a signal handler.\nIn this signal handler, we set the ",[44,532,533],{},"RIP"," to ",[44,536,537],{},"RDI",", which will effectively jump over the last instruction.\nThe last instruction stores ",[44,540,213],{}," in ",[44,543,544],{},"R15B",".\nThis only happens if the previous instruction executed successfully, because it is skipped if the signal handler is invoked.\nThis means that ",[44,547,544],{}," will contain ",[44,550,213],{}," if the instruction exists, or ",[44,553,131],{}," if it does not.",[11,556,557,558,58,561,65,564,566],{},"Now, we have ended up with our fingerprint values in ",[44,559,560],{},"R13B",[44,562,563],{},"R14B",[44,565,544],{},".\nWe can identify the CPU we are running on, by looking up the results in the following table:",[79,568,569,581],{},[82,570,571],{},[85,572,573,575,577,579],{},[88,574,560],{},[88,576,563],{},[88,578,544],{},[88,580,90],{},[108,582,583,593,603,614,625],{},[85,584,585,587,589,591],{},[113,586,131],{},[113,588,131],{},[113,590,131],{},[113,592,93],{},[85,594,595,597,599,601],{},[113,596,131],{},[113,598,131],{},[113,600,213],{},[113,602,97],{},[85,604,605,607,609,611],{},[113,606,213],{},[113,608,131],{},[113,610,213],{},[113,612,613],{},"Intel i9-13900 (p)",[85,615,616,618,620,622],{},[113,617,213],{},[113,619,131],{},[113,621,131],{},[113,623,624],{},"Intel Xeon Silver 4110",[85,626,627,629,631,633],{},[113,628,213],{},[113,630,213],{},[113,632,213],{},[113,634,635],{},"Intel i9-13900 (e)",[11,637,638,639,642],{},"This fingerprinting is hard to detect when hidden within a large binary.\nIt doesn't give any indication that it might be trying to fingerprint the CPU: it doesn't perform any syscalls and doesn't call the ",[44,640,641],{},"CPUID"," instruction.",[15,644,646],{"id":645},"security-implications","Security Implications",[11,648,649,650,655,656,177],{},"Differences in CPU implementations pose a security risk.\nFor example, malware could use these differences to try and detect when it is running in an emulator, and disable itself to evade analysis.\nThis is not new: similar tactics ",[49,651,654],{"href":652,"rel":653},"https:\u002F\u002Fx86.lol\u002Fgeneric\u002F2019\u002F02\u002F08\u002Ffingerprint.html",[53],"have been documented",", and malware ",[49,657,660],{"href":658,"rel":659},"http:\u002F\u002Fweb.archive.org\u002Fweb\u002F20201026062235\u002Fhttps:\u002F\u002Fwww.mysonicwall.com\u002Fsonicalert\u002Fsearchresults.aspx?ev=article&id=1085",[53],"has been known to do this",[11,662,663,664,667],{},"Additionally, these differences can also be used as an obfuscation technique.\nWe can use them to trick decompilers.\nFor example, when viewing this assembly it is clear that it may perform a ",[44,665,666],{},"SYSCALL"," under some conditions:",[239,669,671],{"className":241,"code":670,"language":243,"meta":244,"style":244},".text\n.globl f0\n.globl main\nf0:\n    mov    %rdi,%rax\n    xor    %rdi,%rdi\n    rcl    $0x9,%al\n    jno    skip\n    mov    $0x3c,%al\n    syscall ; \u003C-- HERE!\nskip:               \n    ret\n\nmain:\n    mov $1, %rdi\n    call f0\n    ret\n",[44,672,673,679,687,694,699,715,729,740,748,757,766,774,779,786,792,805,813],{"__ignoreMap":244},[248,674,675],{"class":250,"line":251},[248,676,678],{"class":677},"sScJk",".text\n",[248,680,681,684],{"class":250,"line":268},[248,682,683],{"class":677},".globl",[248,685,686],{"class":262}," f0\n",[248,688,689,691],{"class":250,"line":281},[248,690,683],{"class":677},[248,692,693],{"class":262}," main\n",[248,695,696],{"class":250,"line":295},[248,697,698],{"class":677},"f0:\n",[248,700,701,704,707,710,713],{"class":250,"line":304},[248,702,703],{"class":254},"    mov",[248,705,706],{"class":262},"    %",[248,708,709],{"class":258},"rdi",[248,711,712],{"class":262},",%",[248,714,278],{"class":258},[248,716,717,720,722,724,726],{"class":250,"line":313},[248,718,719],{"class":254},"    xor",[248,721,706],{"class":262},[248,723,709],{"class":258},[248,725,712],{"class":262},[248,727,728],{"class":258},"rdi\n",[248,730,731,734,737],{"class":250,"line":322},[248,732,733],{"class":254},"    rcl",[248,735,736],{"class":262},"    $0x9,%",[248,738,739],{"class":258},"al\n",[248,741,742,745],{"class":250,"line":340},[248,743,744],{"class":254},"    jno",[248,746,747],{"class":262},"    skip\n",[248,749,750,752,755],{"class":250,"line":353},[248,751,703],{"class":254},[248,753,754],{"class":262},"    $0x3c,%",[248,756,739],{"class":258},[248,758,759,762],{"class":250,"line":373},[248,760,761],{"class":254},"    syscall",[248,763,765],{"class":764},"sJ8bj"," ; \u003C-- HERE!\n",[248,767,768,771],{"class":250,"line":385},[248,769,770],{"class":677},"skip:",[248,772,773],{"class":262},"               \n",[248,775,776],{"class":250,"line":394},[248,777,778],{"class":254},"    ret\n",[248,780,782],{"class":250,"line":781},13,[248,783,785],{"emptyLinePlaceholder":784},true,"\n",[248,787,789],{"class":250,"line":788},14,[248,790,791],{"class":677},"main:\n",[248,793,795,797,800,803],{"class":250,"line":794},15,[248,796,703],{"class":254},[248,798,799],{"class":258}," $1",[248,801,802],{"class":262},", %",[248,804,728],{"class":258},[248,806,808,811],{"class":250,"line":807},16,[248,809,810],{"class":254},"    call",[248,812,686],{"class":262},[248,814,816],{"class":250,"line":815},17,[248,817,778],{"class":254},[11,819,820,821,824,825,833],{},"This code relies on undefined behavior in the ",[44,822,823],{},"RCL"," instruction that differs between Intel and AMD CPUs.\nOn an AMD 3900X, the syscall is always executed.\nHowever, there are quite a few decompilers that will happily ",[49,826,829,830],{"href":827,"rel":828},"https:\u002F\u002Fdogbolt.org\u002F?id=060ea669-3df1-45ac-893f-60f736e78466",[53],"hide the ",[44,831,832],{},"syscall"," in their output, because they make assuptions about undefined behavior that are incorrect.\nFor example, here is the output of Hex-Rays:",[239,835,839],{"className":836,"code":837,"language":838,"meta":244,"style":244},"language-c shiki shiki-themes github-light github-dark","char __fastcall f0(char a1)\n{\n  char result; \u002F\u002F al\n\n  _AL = a1;\n  __asm { rcl     al, 9 }\n  return result;\n}\n","c",[44,840,841,864,869,880,884,895,906,914],{"__ignoreMap":244},[248,842,843,846,849,852,855,857,861],{"class":250,"line":251},[248,844,845],{"class":254},"char",[248,847,848],{"class":262}," __fastcall ",[248,850,851],{"class":677},"f0",[248,853,854],{"class":262},"(",[248,856,845],{"class":254},[248,858,860],{"class":859},"s4XuR"," a1",[248,862,863],{"class":262},")\n",[248,865,866],{"class":250,"line":268},[248,867,868],{"class":262},"{\n",[248,870,871,874,877],{"class":250,"line":281},[248,872,873],{"class":254},"  char",[248,875,876],{"class":262}," result;",[248,878,879],{"class":764}," \u002F\u002F al\n",[248,881,882],{"class":250,"line":295},[248,883,785],{"emptyLinePlaceholder":784},[248,885,886,889,892],{"class":250,"line":304},[248,887,888],{"class":262},"  _AL ",[248,890,891],{"class":254},"=",[248,893,894],{"class":262}," a1;\n",[248,896,897,900,903],{"class":250,"line":313},[248,898,899],{"class":262},"  __asm { rcl     al, ",[248,901,902],{"class":258},"9",[248,904,905],{"class":262}," }\n",[248,907,908,911],{"class":250,"line":322},[248,909,910],{"class":254},"  return",[248,912,913],{"class":262}," result;\n",[248,915,916],{"class":250,"line":340},[248,917,918],{"class":262},"}\n",[11,920,921],{},"Such output may lead us to falsely assume that this code does not perform any syscalls.",[11,923,924],{},"The attack surface for these obfuscation tricks is huge: any instruction interpreted incorrectly by analysis tools can be used, whether that is because of undefined behavior or incorrect implementation of semantics.\nTo detect this, more accurate instruction semantics are needed, that also describe undefined behavior.",[11,926,927,928,933,934,939,940,945],{},"This post was based on ",[49,929,932],{"href":930,"rel":931},"https:\u002F\u002Fliblisa.nl\u002Fpublications#oopsla24",[53],"my paper"," published ",[49,935,938],{"href":936,"rel":937},"https:\u002F\u002F2024.splashcon.org\u002Ftrack\u002Fsplash-2024-OOPSLA#program:~:text=libLISA,Tech,-DOI",[53],"at OOPSLA'24",".\nIt describes ",[49,941,944],{"href":942,"rel":943},"https:\u002F\u002Fliblisa.nl\u002F",[53],"libLISA",", a tool that aims to obtain accurate semantics by using an actual CPU as the ground truth.\nlibLISA can fully automatically analyze a CPU, enumerate all instructions, and synthesize semantics for many of them.\nThe generated semantics can then either be used directly in binary analysis tools, or be compared with existing implementations to find differences and bugs.",[947,948,949],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":244,"searchDepth":268,"depth":268,"links":951},[952,953,954],{"id":17,"depth":268,"text":18},{"id":161,"depth":268,"text":162},{"id":645,"depth":268,"text":646},"2026-07-07","The x86 architecture is arguably the most complex general-purpose Instruction Set Architecture ever made. It has thousands of different instructions (depending on how you count). Some instructions or specific instruction outputs are undefined, which means they are allowed to behave in any way the CPU manufacturer sees fit. This complexity can be (ab)used to fingerprint CPUs.","md",{},"\u002Fposts\u002F2026-07\u002Ffingerprinting-cpus",{"title":6,"description":956},"posts\u002F2026-07\u002Ffingerprinting-cpus","Tas7z3XYcYpFXU6cdot__ppYtvcngf7ZPNY-gIewL3w",1783361375320]