{"id":4825,"date":"2024-10-07T11:09:25","date_gmt":"2024-10-07T19:09:25","guid":{"rendered":"https:\/\/www.pnfsoftware.com\/blog\/?p=4825"},"modified":"2024-10-07T11:09:27","modified_gmt":"2024-10-07T19:09:27","slug":"deobfuscation-ratings-inlining-fat-functions-and-breaking-opaque-predicates","status":"publish","type":"post","link":"https:\/\/www.pnfsoftware.com\/blog\/deobfuscation-ratings-inlining-fat-functions-and-breaking-opaque-predicates\/","title":{"rendered":"Deobfuscation ratings, inlining &#8220;fat&#8221; functions, and breaking opaque predicates"},"content":{"rendered":"\n<p>In this post, we are having a quick look at a relatively novel protection techniques found in the wild. The class we are looking at is <code>com.X<\/code> (SHA256: a519e4a20586807665d82ea28892e2ede184807868552f23210bf10c05727980).<\/p>\n\n\n\n<p>Have a look at the decompiled code, with standard JEB options. It was auto-deobfuscated and thoroughly cleaned by <code>dexdec<\/code>, JEB&#8217;s Dalvik decompiler:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"568\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-1-1024x568.png\" alt=\"\" class=\"wp-image-4828\" srcset=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-1-1024x568.png 1024w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-1-300x166.png 300w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-1-768x426.png 768w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-1-1536x852.png 1536w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-1-2048x1136.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Decompilation of com.X with standard options (it&#8217;s been deobfuscated, and JEB is letting you know about it by providing deobfuscation ratings or scores as method comments)<\/figcaption><\/figure>\n\n\n\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_82_2 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<label for=\"ez-toc-cssicon-toggle-item-69d6acd347a37\" class=\"ez-toc-cssicon-toggle-label\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/label><input type=\"checkbox\"  id=\"ez-toc-cssicon-toggle-item-69d6acd347a37\"  aria-label=\"Toggle\" \/><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/www.pnfsoftware.com\/blog\/deobfuscation-ratings-inlining-fat-functions-and-breaking-opaque-predicates\/#A_note_on_deobfuscation_ratings\" >A note on deobfuscation ratings<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/www.pnfsoftware.com\/blog\/deobfuscation-ratings-inlining-fat-functions-and-breaking-opaque-predicates\/#Inlining_%E2%80%9Cfat%E2%80%9D_functions\" >Inlining &#8220;fat&#8221; functions<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/www.pnfsoftware.com\/blog\/deobfuscation-ratings-inlining-fat-functions-and-breaking-opaque-predicates\/#Resolving_opaque_predicates\" >Resolving opaque predicates<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/www.pnfsoftware.com\/blog\/deobfuscation-ratings-inlining-fat-functions-and-breaking-opaque-predicates\/#Conclusion\" >Conclusion<\/a><\/li><\/ul><\/nav><\/div>\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"A_note_on_deobfuscation_ratings\"><\/span>A note on deobfuscation ratings<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Two items to notice:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Some methods outputs are collapsed: their direct output was deemed useless because their code were inlined in corresponding callers. You may re-expand them with the <strong>Dash<\/strong> (-) action key, or via the <em>Action<\/em> menu, <em>Collapse\/Expand<\/em> command.<\/li>\n\n\n\n<li>Some decompiled methods have an auto-comment specifying a deobfuscation rating and score. This score is calculated from the result of IR optimizers <a href=\"https:\/\/www.pnfsoftware.com\/jeb\/apidoc\/reference\/com\/pnfsoftware\/jeb\/core\/units\/code\/android\/ir\/IDOptimizer.html#DEOBFUSCATOR\">tagged as DEOBFUSCATOR<\/a>. If the score reaches a threshold, the rating (<code>LOW<\/code> &#8211; not shown-, <code>MEDIUM<\/code>, <code>HIGH<\/code>, <code>EXTRA<\/code>) is specified in the decompilation output, to give a hint to the user that the low-level code is protected, and that the high-level decomp was deobfuscated and cleaned.<\/li>\n<\/ul>\n\n\n\n<p>The deobfuscation ratings for several methods of <code>com.X<\/code> are high. It looks like this class received a significant amount of protection. However, after clean-up, the meaningful code consists of two one-liner methods: one storing a timestamp (method <code>gg<\/code>), the other one calculating an elapsed time (method <code>gf<\/code>).<\/p>\n\n\n\n<p>Let&#8217;s have a look at the decompiled code with deobfuscators disabled: Redecompile the code with <strong>CMD1+TAB<\/strong> (<em>Action<\/em> menu, <em>Decompile with Options&#8230;<\/em>), and untick <em>&#8220;Enable deobfuscator optimizers&#8221;<\/em>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"457\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-2-1024x457.png\" alt=\"\" class=\"wp-image-4829\" srcset=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-2-1024x457.png 1024w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-2-300x134.png 300w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-2-768x342.png 768w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-2-1536x685.png 1536w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-2-2048x913.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">dexdec options when redecompiling with <em>Action<\/em>, <em>Decompile with options&#8230;<\/em><\/figcaption><\/figure>\n\n\n\n<p>The re-decompilation result is as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-3.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"932\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-3-1024x932.png\" alt=\"\" class=\"wp-image-4831\" srcset=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-3-1024x932.png 1024w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-3-300x273.png 300w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-3-768x699.png 768w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-3-1536x1398.png 1536w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-3.png 2000w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Decompilation of com.X with deobfuscators disabled<\/figcaption><\/figure>\n\n\n\n<p>There is quite a lot to look at here, mainly, the <strong>fat routines<\/strong> and the <strong>opaque predicates<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Inlining_%E2%80%9Cfat%E2%80%9D_functions\"><\/span>Inlining &#8220;fat&#8221; functions<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>We see that <code>gf<\/code> calls <code>new<\/code> with a set of fixed integer (<code>v<\/code>, <code>v1<\/code>) as well as the<code> identityHashCode<\/code> of itself (<code>v3<\/code>, essentially a pseudo-random number). Similarly, <code>gg<\/code> also redirects to <code>new<\/code>, with a different set of arguments.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-4.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"183\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-4-1024x183.png\" alt=\"\" class=\"wp-image-4833\" srcset=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-4-1024x183.png 1024w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-4-300x54.png 300w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-4-768x137.png 768w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-4.png 1244w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">The methods gf() and gg() are wrappers calling the method new()  with various keys<\/figcaption><\/figure>\n\n\n\n<p>A quick examination of <code>new<\/code> shows that two code paths may be executed, based on the values of the provided triplet (v, v1, v2):<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-5.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"215\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-5-1024x215.png\" alt=\"\" class=\"wp-image-4834\" srcset=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-5-1024x215.png 1024w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-5-300x63.png 300w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-5-768x161.png 768w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-5-1536x322.png 1536w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-5.png 1758w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Decompilation of the synthetic &#8220;fat&#8221; function new, holding the real code of gf and gg<\/figcaption><\/figure>\n\n\n\n<p>So, what happened? The protection of class <code>com.X<\/code> consisted of taking the bodies of code of <code>gf<\/code> and <code>gg<\/code>, merge them into a single method <code>new<\/code> (hence the name &#8220;fat&#8221;), and change the codes of <code>gf<\/code> and <code>gg<\/code> to trampoline into <code>new<\/code> with selectors to execute the proper code.<\/p>\n\n\n\n<p>Here is an easier representation of that process, with a single selector (instead of a triplet):<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n\/\/ UNPROTECTED CLASS C\nclass C \n  int fld1;\n  \n  int f1(int x) {\n    return 25 + x;\n  }\n  \n  int f2() {\n    return 31 * fld1;\n  }\n}\n\n\/\/ PROTECTED CLASS C\nclass protected_C\n  int fld1;\n\n  int f1(int x) {\n    return (int)fat_routine(new Object&#x5B;]{this, x}, 1);\n  }\n  \n  int f2() {\n    return (int)fat_routine(new Object&#x5B;]{this}, 2);\n  }\n  \n  static Object fat_routine(Object&#x5B;] params, int selector) {\n    if(selector == 1) {\n      return 25 + (int)params&#x5B;1];\n    }\n    else if(selector == 2) {\n      return 31 * ((C)params&#x5B;0]).fld1;\n    }\n    throw new RuntimeException();  \/\/ should not happen\n  }\n}\n<\/pre><\/div>\n\n\n<p>Although the above code is trivial, we can use it to highlights two complications the decompiler will face when dealing with the more complex implementations made by the a real code protection system:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>When to decide to inline, i.e. how to detect fat functions? (that question is outside the scope of this blog, and would not be of much interest to most readers)<\/li>\n\n\n\n<li>What about complex selectors, such as a triplet with a pseudo-random int?<\/li>\n<\/ul>\n\n\n\n<p>If JEB&#8217;s <code>dexdec<\/code> were to inline the calls to <code>new<\/code> as it is, we&#8217;d end up with the following decomps &#8211; not quite what we saw at the beginning of this article!<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-6.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"393\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-6-1024x393.png\" alt=\"\" class=\"wp-image-4836\" srcset=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-6-1024x393.png 1024w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-6-300x115.png 300w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-6-768x295.png 768w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-6-1536x589.png 1536w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-6.png 1887w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Decompilation the deobfuscators re-enabled, however the opaque predicate breaker was disabled<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Resolving_opaque_predicates\"><\/span>Resolving opaque predicates<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Let&#8217;s look at method <code>gf<\/code>. We can see that the pseudo-random selector, after inlining, is used to calculate a predicate that will determine which path to take, i.e. do we execute the actual code for <code>gf<\/code>, or the code for <code>gg<\/code>?<\/p>\n\n\n\n<p>The predicate seen in <code>gf<\/code> can be re-written as:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">PRED = 0xDE9B00B0 + (~(0x99525D4B | X) | 1 &amp; X) * 520 + (0x99525D4B &amp; X | ~(X | 0x66ADA2B5)) * -1040 + (0x99525D4A | 0x66ADA2B5 &amp; X | ~(X | 0x66ADA2B5)) * 520 != 1<\/pre>\n\n\n\n<p>Internally, JEB does quite a bit to simplify it, and ultimately, when all fast reductions and simplifications are applied, it will use the well-known <a href=\"https:\/\/en.wikipedia.org\/wiki\/Z3_Theorem_Prover\">Z3 SMT solver<\/a> to break the predicate. In this case, regardless of the value of X, the predicate is true. Therefore, <code>gf<\/code> will be simplified to:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>return X.iz(arr_object);<\/code><\/pre>\n\n\n\n<p>(Note that method <code>iz<\/code> is itself a candidate for inlining! At the end, the cleaned-up code shown in the introduction of this article will be generated.)<\/p>\n\n\n\n<p>The use of Z3 and other external theorem provers that may be used by JEB and its plugins can be disabled in the option (see <em>&#8220;Enable predicate breaker&#8221;<\/em>):<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-7.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"194\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-7-1024x194.png\" alt=\"\" class=\"wp-image-4838\" srcset=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-7-1024x194.png 1024w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-7-300x57.png 300w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-7-768x145.png 768w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-7-1536x291.png 1536w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2024\/10\/image-7-2048x388.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">The external predicate solver can be disabled in the options<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Conclusion\"><\/span>Conclusion<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>We hope this quick note will shed some light on some newer features or recent upgrades that went into <code>dexdec<\/code>. Many of those were already present in <code>gendec<\/code>, the generic decompiler used for anything non-Dalvik, and it was about time to add those advanced clean-up passes into the Dalvik decompiler as well. In a sense, <code>dexdec<\/code> has caught up and even gone further than <code>gendec<\/code> on these aspects.<\/p>\n\n\n\n<p>Which leads me to say there will likely be a Part 2 or at least an update for this blog, to highlight another complex deobfuscating task: the simplification of arithmetic operations consisting of bitwise operations and mixed boolean\/arithmetic (MBA) expressions.<\/p>\n\n\n\n<p>Stay tuned! Thank you to all our users and readers of this blog \ud83d\ude42 Do not hesitate to reach out through the usual channels (<a href=\"https:\/\/www.pnfsoftware.com\/chat\">Slack<\/a>, <a href=\"mailto:support@pnfsoftware.com\">email<\/a>, <a href=\"https:\/\/x.com\/jebdec\">X<\/a>).<\/p>\n\n\n\n<p>&#8211; Nicolas<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post, we are having a quick look at a relatively novel protection techniques found in the wild. The class we are looking at is com.X (SHA256: a519e4a20586807665d82ea28892e2ede184807868552f23210bf10c05727980). Have a look at the decompiled code, with standard JEB options. It was auto-deobfuscated and thoroughly cleaned by dexdec, JEB&#8217;s Dalvik decompiler: A note on deobfuscation &hellip; <a href=\"https:\/\/www.pnfsoftware.com\/blog\/deobfuscation-ratings-inlining-fat-functions-and-breaking-opaque-predicates\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Deobfuscation ratings, inlining &#8220;fat&#8221; functions, and breaking opaque predicates<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3,30,5],"tags":[],"class_list":["post-4825","post","type-post","status-publish","format-standard","hentry","category-decompilation","category-jeb5","category-obfuscation"],"_links":{"self":[{"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/posts\/4825","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/comments?post=4825"}],"version-history":[{"count":0,"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/posts\/4825\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/media?parent=4825"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/categories?post=4825"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/tags?post=4825"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}