denyhosts/clamscan/libclamav/autoit.c
2022-10-22 18:41:00 +08:00

1537 lines
47 KiB
C

/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2007-2013 Sourcefire, Inc.
*
* Authors: Alberto Wu
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#if HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "clamav.h"
#include "others.h"
#include "scanners.h"
#include "autoit.h"
#include "fmap.h"
#include "fpu.h"
static int fpu_words = FPU_ENDIAN_INITME;
const char *autoit_functions[] = {
"ABS",
"ACOS",
"ADLIBREGISTER",
"ADLIBUNREGISTER",
"ASC",
"ASCW",
"ASIN",
"ASSIGN",
"ATAN",
"AUTOITSETOPTION",
"AUTOITWINGETTITLE",
"AUTOITWINSETTITLE",
"BEEP",
"BINARY",
"BINARYLEN",
"BINARYMID",
"BINARYTOSTRING",
"BITAND",
"BITNOT",
"BITOR",
"BITROTATE",
"BITSHIFT",
"BITXOR",
"BLOCKINPUT",
"BREAK",
"CALL",
"CDTRAY",
"CEILING",
"CHR",
"CHRW",
"CLIPGET",
"CLIPPUT",
"CONSOLEREAD",
"CONSOLEWRITE",
"CONSOLEWRITEERROR",
"CONTROLCLICK",
"CONTROLCOMMAND",
"CONTROLDISABLE",
"CONTROLENABLE",
"CONTROLFOCUS",
"CONTROLGETFOCUS",
"CONTROLGETHANDLE",
"CONTROLGETPOS",
"CONTROLGETTEXT",
"CONTROLHIDE",
"CONTROLLISTVIEW",
"CONTROLMOVE",
"CONTROLSEND",
"CONTROLSETTEXT",
"CONTROLSHOW",
"CONTROLTREEVIEW",
"COS",
"DEC",
"DIRCOPY",
"DIRCREATE",
"DIRGETSIZE",
"DIRMOVE",
"DIRREMOVE",
"DLLCALL",
"DLLCALLADDRESS",
"DLLCALLBACKFREE",
"DLLCALLBACKGETPTR",
"DLLCALLBACKREGISTER",
"DLLCLOSE",
"DLLOPEN",
"DLLSTRUCTCREATE",
"DLLSTRUCTGETDATA",
"DLLSTRUCTGETPTR",
"DLLSTRUCTGETSIZE",
"DLLSTRUCTSETDATA",
"DRIVEGETDRIVE",
"DRIVEGETFILESYSTEM",
"DRIVEGETLABEL",
"DRIVEGETSERIAL",
"DRIVEGETTYPE",
"DRIVEMAPADD",
"DRIVEMAPDEL",
"DRIVEMAPGET",
"DRIVESETLABEL",
"DRIVESPACEFREE",
"DRIVESPACETOTAL",
"DRIVESTATUS",
"ENVGET",
"ENVSET",
"ENVUPDATE",
"EVAL",
"EXECUTE",
"EXP",
"FILECHANGEDIR",
"UNKNOWN_89",
"FILECLOSE",
"FILECOPY",
"FILECREATENTFSLINK",
"FILECREATESHORTCUT",
"FILEDELETE",
"FILEEXISTS",
"FILEFINDFIRSTFILE",
"FILEFINDNEXTFILE",
"FILEFLUSH",
"FILEGETATTRIB",
"FILEGETENCODING",
"FILEGETLONGNAME",
"FILEGETPOS",
"FILEGETSHORTCUT",
"FILEGETSHORTNAME",
"FILEGETSIZE",
"FILEGETTIME",
"FILEGETVERSION",
"FILEINSTALL",
"FILEMOVE",
"FILEOPEN",
"FILEOPENDIALOG",
"FILEREAD",
"FILEREADLINE",
"FILEREADTOARRAY",
"FILERECYCLE",
"FILERECYCLEEMPTY",
"FILESAVEDIALOG",
"FILESELECTFOLDER",
"FILESETATTRIB",
"FILESETEND",
"FILESETPOS",
"FILESETTIME",
"FILEWRITE",
"FILEWRITELINE",
"FLOOR",
"FTPSETPROXY",
"FUNCNAME",
"GUICREATE",
"GUICTRLCREATEAVI",
"GUICTRLCREATEBUTTON",
"GUICTRLCREATECHECKBOX",
"GUICTRLCREATECOMBO",
"GUICTRLCREATECONTEXTMENU",
"GUICTRLCREATEDATE",
"GUICTRLCREATEDUMMY",
"GUICTRLCREATEEDIT",
"GUICTRLCREATEGRAPHIC",
"GUICTRLCREATEGROUP",
"GUICTRLCREATEICON",
"GUICTRLCREATEINPUT",
"GUICTRLCREATELABEL",
"GUICTRLCREATELIST",
"GUICTRLCREATELISTVIEW",
"GUICTRLCREATELISTVIEWITEM",
"GUICTRLCREATEMENU",
"GUICTRLCREATEMENUITEM",
"GUICTRLCREATEMONTHCAL",
"GUICTRLCREATEOBJ",
"GUICTRLCREATEPIC",
"GUICTRLCREATEPROGRESS",
"GUICTRLCREATERADIO",
"GUICTRLCREATESLIDER",
"GUICTRLCREATETAB",
"GUICTRLCREATETABITEM",
"GUICTRLCREATETREEVIEW",
"GUICTRLCREATETREEVIEWITEM",
"GUICTRLCREATEUPDOWN",
"GUICTRLDELETE",
"GUICTRLGETHANDLE",
"GUICTRLGETSTATE",
"GUICTRLREAD",
"GUICTRLRECVMSG",
"GUICTRLREGISTERLISTVIEWSORT",
"GUICTRLSENDMSG",
"GUICTRLSENDTODUMMY",
"GUICTRLSETBKCOLOR",
"GUICTRLSETCOLOR",
"GUICTRLSETCURSOR",
"GUICTRLSETDATA",
"GUICTRLSETDEFBKCOLOR",
"GUICTRLSETDEFCOLOR",
"GUICTRLSETFONT",
"GUICTRLSETGRAPHIC",
"GUICTRLSETIMAGE",
"GUICTRLSETLIMIT",
"GUICTRLSETONEVENT",
"GUICTRLSETPOS",
"GUICTRLSETRESIZING",
"GUICTRLSETSTATE",
"GUICTRLSETSTYLE",
"GUICTRLSETTIP",
"GUIDELETE",
"GUIGETCURSORINFO",
"GUIGETMSG",
"GUIGETSTYLE",
"GUIREGISTERMSG",
"GUISETACCELERATORS",
"GUISETBKCOLOR",
"GUISETCOORD",
"GUISETCURSOR",
"GUISETFONT",
"GUISETHELP",
"GUISETICON",
"GUISETONEVENT",
"GUISETSTATE",
"GUISETSTYLE",
"GUISTARTGROUP",
"GUISWITCH",
"HEX",
"HOTKEYSET",
"HTTPSETPROXY",
"HTTPSETUSERAGENT",
"HWND",
"INETCLOSE",
"INETGET",
"INETGETINFO",
"INETGETSIZE",
"INETREAD",
"INIDELETE",
"INIREAD",
"INIREADSECTION",
"INIREADSECTIONNAMES",
"INIRENAMESECTION",
"INIWRITE",
"INIWRITESECTION",
"INPUTBOX",
"INT",
"ISADMIN",
"ISARRAY",
"ISBINARY",
"ISBOOL",
"ISDECLARED",
"ISDLLSTRUCT",
"ISFLOAT",
"ISFUNC",
"ISHWND",
"ISINT",
"ISKEYWORD",
"UNKNOWN_229",
"ISNUMBER",
"ISOBJ",
"ISPTR",
"ISSTRING",
"LOG",
"MEMGETSTATS",
"UNKNOWN_235",
"UNKNOWN_236",
"UNKNOWN_237",
"UNKNOWN_238",
"MOD",
"MOUSECLICK",
"MOUSECLICKDRAG",
"MOUSEDOWN",
"MOUSEGETCURSOR",
"MOUSEGETPOS",
"MOUSEMOVE",
"MOUSEUP",
"MOUSEWHEEL",
"MSGBOX",
"NUMBER",
"OBJCREATE",
"OBJCREATEINTERFACE",
"OBJEVENT",
"OBJGET",
"OBJNAME",
"ONAUTOITEXITREGISTER",
"ONAUTOITEXITUNREGISTER",
"OPT",
"PING",
"PIXELCHECKSUM",
"PIXELGETCOLOR",
"PIXELSEARCH",
"PROCESSCLOSE",
"PROCESSEXISTS",
"PROCESSGETSTATS",
"PROCESSLIST",
"PROCESSSETPRIORITY",
"PROCESSWAIT",
"PROCESSWAITCLOSE",
"PROGRESSOFF",
"PROGRESSON",
"PROGRESSSET",
"PTR",
"RANDOM",
"REGDELETE",
"REGENUMKEY",
"REGENUMVAL",
"REGREAD",
"REGWRITE",
"ROUND",
"RUN",
"RUNAS",
"RUNASWAIT",
"RUNWAIT",
"SEND",
"SENDKEEPACTIVE",
"SETERROR",
"SETEXTENDED",
"SHELLEXECUTE",
"SHELLEXECUTEWAIT",
"SHUTDOWN",
"SIN",
"SLEEP",
"SOUNDPLAY",
"SOUNDSETWAVEVOLUME",
"SPLASHIMAGEON",
"SPLASHOFF",
"SPLASHTEXTON",
"SQRT",
"SRANDOM",
"STATUSBARGETTEXT",
"STDERRREAD",
"STDINWRITE",
"STDIOCLOSE",
"STDOUTREAD",
"STRING",
"STRINGADDCR",
"STRINGCOMPARE",
"STRINGFORMAT",
"STRINGFROMASCIIARRAY",
"STRINGINSTR",
"STRINGISALNUM",
"STRINGISALPHA",
"STRINGISASCII",
"STRINGISDIGIT",
"STRINGISFLOAT",
"STRINGISINT",
"STRINGISLOWER",
"STRINGISSPACE",
"STRINGISUPPER",
"STRINGISXDIGIT",
"STRINGLEFT",
"STRINGLEN",
"STRINGLOWER",
"STRINGMID",
"STRINGREGEXP",
"STRINGREGEXPREPLACE",
"STRINGREPLACE",
"STRINGREVERSE",
"STRINGRIGHT",
"STRINGSPLIT",
"STRINGSTRIPCR",
"STRINGSTRIPWS",
"STRINGTOASCIIARRAY",
"STRINGTOBINARY",
"STRINGTRIMLEFT",
"STRINGTRIMRIGHT",
"STRINGUPPER",
"TAN",
"TCPACCEPT",
"TCPCLOSESOCKET",
"TCPCONNECT",
"TCPLISTEN",
"TCPNAMETOIP",
"TCPRECV",
"TCPSEND",
"TCPSHUTDOWN,",
"TCPSTARTUP,",
"TIMERDIFF",
"TIMERINIT",
"TOOLTIP",
"TRAYCREATEITEM",
"TRAYCREATEMENU",
"TRAYGETMSG",
"TRAYITEMDELETE",
"TRAYITEMGETHANDLE",
"TRAYITEMGETSTATE",
"TRAYITEMGETTEXT",
"TRAYITEMSETONEVENT",
"TRAYITEMSETSTATE",
"TRAYITEMSETTEXT",
"TRAYSETCLICK",
"TRAYSETICON",
"TRAYSETONEVENT",
"TRAYSETPAUSEICON",
"TRAYSETSTATE",
"TRAYSETTOOLTIP",
"TRAYTIP",
"UBOUND",
"UDPBIND",
"UDPCLOSESOCKET",
"UDPOPEN",
"UDPRECV",
"UDPSEND",
"UNKNOWN_375",
"UNKNOWN_376",
"VARGETTYPE",
"WINACTIVATE",
"WINACTIVE",
"WINCLOSE",
"WINEXISTS",
"WINFLASH",
"WINGETCARETPOS",
"WINGETCLASSLIST",
"WINGETCLIENTSIZE",
"WINGETHANDLE",
"WINGETPOS",
"WINGETPROCESS",
"WINGETSTATE",
"WINGETTEXT",
"WINGETTITLE",
"WINKILL",
"WINLIST",
"WINMENUSELECTITEM",
"WINMINIMIZEALL",
"WINMINIMIZEALLUNDO",
"WINMOVE",
"WINSETONTOP",
"WINSETSTATE",
"WINSETTITLE",
"WINSETTRANS",
"WINWAIT",
"WINWAITACTIVE",
"WINWAITCLOSE",
"WINWAITNOTACTIVE"};
const char *autoit_keywords[] = {
"UNKNOWN_0",
"AND",
"OR",
"NOT",
"IF",
"THEN",
"ELSE",
"ELSEIF",
"ENDIF",
"WHILE",
"WEND",
"DO",
"UNTIL",
"FOR",
"NEXT",
"TO",
"STEP",
"IN",
"EXITLOOP",
"CONTINUELOOP",
"SELECT",
"CASE",
"ENDSELECT",
"SWITCH",
"ENDSWITCH",
"CONTINUECASE",
"DIM",
"REDIM",
"LOCAL",
"GLOBAL",
"CONST",
"STATIC",
"FUNC",
"ENDFUNC",
"RETURN",
"EXIT",
"BYREF",
"WITH",
"ENDWITH",
"TRUE",
"FALSE",
"DEFAULT",
"NULL",
"UNKNOWN_43",
"ENUM",
};
/* FIXME: use unicode detection and normalization from edwin */
static unsigned int u2a(uint8_t *dest, unsigned int len)
{
uint8_t *src = dest;
unsigned int i, j;
if (len < 2)
return len;
if (len > 4 && src[0] == 0xff && src[1] == 0xfe && src[2]) {
len -= 2;
src += 2;
} else {
unsigned int cnt = 0;
j = (len > 20) ? 20 : (len & ~1);
for (i = 0; i < j; i += 2)
cnt += (src[i] != 0 && src[i + 1] == 0);
if (cnt * 4 < j)
return len;
}
j = len;
len >>= 1;
for (i = 0; i < j; i += 2)
*dest++ = src[i];
return len;
}
/*********************
MT realted stuff
*********************/
struct MT {
uint32_t *next;
uint32_t items;
uint32_t mt[624];
};
static uint8_t MT_getnext(struct MT *MT)
{
uint32_t r;
if (!--MT->items) {
uint32_t *mt = MT->mt;
unsigned int i;
MT->items = 624;
MT->next = mt;
for (i = 0; i < 227; i++)
mt[i] = ((((mt[i] ^ mt[i + 1]) & 0x7ffffffe) ^ mt[i]) >> 1) ^ ((0 - (mt[i + 1] & 1)) & 0x9908b0df) ^ mt[i + 397];
for (; i < 623; i++)
mt[i] = ((((mt[i] ^ mt[i + 1]) & 0x7ffffffe) ^ mt[i]) >> 1) ^ ((0 - (mt[i + 1] & 1)) & 0x9908b0df) ^ mt[i - 227];
mt[623] = ((((mt[623] ^ mt[0]) & 0x7ffffffe) ^ mt[623]) >> 1) ^ ((0 - (mt[0] & 1)) & 0x9908b0df) ^ mt[i - 227];
}
r = *(MT->next++);
r ^= (r >> 11);
r ^= ((r & 0xff3a58ad) << 7);
r ^= ((r & 0xffffdf8c) << 15);
r ^= (r >> 18);
return (uint8_t)(r >> 1);
}
static void MT_decrypt(uint8_t *buf, unsigned int size, uint32_t seed)
{
struct MT MT;
unsigned int i;
uint32_t *mt = MT.mt;
*mt = seed;
for (i = 1; i < 624; i++)
mt[i] = i + 0x6c078965 * ((mt[i - 1] >> 30) ^ mt[i - 1]);
MT.items = 1;
MT.next = MT.mt;
while (size--)
*buf++ ^= MT_getnext(&MT);
}
/*********************
inflate stuff
*********************/
struct UNP {
uint8_t *outputbuf;
uint8_t *inputbuf;
uint32_t cur_output;
uint32_t cur_input;
uint32_t usize;
uint32_t csize;
uint32_t bits_avail;
union {
uint32_t full;
struct {
#if WORDS_BIGENDIAN != 0
uint16_t h; /* BE */
uint16_t l;
#else
uint16_t l; /* LE */
uint16_t h;
#endif
} half;
} bitmap;
uint32_t error;
};
static uint32_t getbits(struct UNP *UNP, uint32_t size)
{
//cli_dbgmsg("In getbits, (size: %u, bits_avail: %u, UNP->cur_input: %u)\n", size, UNP->bits_avail, UNP->cur_input);
UNP->bitmap.half.h = 0;
if (size > UNP->bits_avail && ((size - UNP->bits_avail - 1) / 16 + 1) * 2 > UNP->csize - UNP->cur_input) {
cli_dbgmsg("autoit: getbits() - not enough bits available\n");
UNP->error = 1;
return 0; /* won't infloop nor spam */
}
while (size) {
if (!UNP->bits_avail) {
//cli_dbgmsg("cur_input: %u (size: %u)\n", UNP->cur_input, size);
UNP->bitmap.half.l |= UNP->inputbuf[UNP->cur_input++] << 8;
UNP->bitmap.half.l |= UNP->inputbuf[UNP->cur_input++];
UNP->bits_avail = 16;
}
UNP->bitmap.full <<= 1;
UNP->bits_avail--;
size--;
}
return (uint32_t)UNP->bitmap.half.h;
}
/*********************
autoit3 EA05 handler
*********************/
static int ea05(cli_ctx *ctx, const uint8_t *base, char *tmpd)
{
uint8_t b[300], comp;
uint32_t s, m4sum = 0;
int i, ret, det = 0;
unsigned int files = 0;
char tempfile[1024];
struct UNP UNP;
fmap_t *map = ctx->fmap;
if (!fmap_need_ptr_once(map, base, 16))
return CL_CLEAN;
for (i = 0; i < 16; i++)
m4sum += *base++;
while ((ret = cli_checklimits("autoit", ctx, 0, 0, 0)) == CL_CLEAN) {
if (!fmap_need_ptr_once(map, base, 8))
return (det ? CL_VIRUS : CL_CLEAN);
/* MT_decrypt(buf,4,0x16fa); waste of time */
if ((uint32_t)cli_readint32(base) != 0xceb06dff) {
cli_dbgmsg("autoit: no FILE magic found, extraction complete\n");
return (det ? CL_VIRUS : CL_CLEAN);
}
s = cli_readint32(base + 4) ^ 0x29bc;
if ((int32_t)s < 0)
return (det ? CL_VIRUS : CL_CLEAN); /* the original code wouldn't seek back here */
base += 8;
if (cli_debug_flag && s < sizeof(b)) {
if (!fmap_need_ptr_once(map, base, s))
return (det ? CL_VIRUS : CL_CLEAN);
memcpy(b, base, s);
MT_decrypt(b, s, s + 0xa25e);
b[s] = '\0';
cli_dbgmsg("autoit: magic string '%s'\n", b);
}
base += s;
if (!fmap_need_ptr_once(map, base, 4))
return (det ? CL_VIRUS : CL_CLEAN);
s = cli_readint32(base) ^ 0x29ac;
if ((int32_t)s < 0)
return (det ? CL_VIRUS : CL_CLEAN); /* the original code wouldn't seek back here */
base += 4;
if (cli_debug_flag && s < sizeof(b)) {
if (!fmap_need_ptr_once(map, base, s))
return (det ? CL_VIRUS : CL_CLEAN);
memcpy(b, base, s);
MT_decrypt(b, s, s + 0xf25e);
b[s] = '\0';
cli_dbgmsg("autoit: original filename '%s'\n", b);
}
base += s;
if (!fmap_need_ptr_once(map, base, 13))
return (det ? CL_VIRUS : CL_CLEAN);
comp = *base;
UNP.csize = cli_readint32(base + 1) ^ 0x45aa;
if ((int32_t)UNP.csize < 0) {
cli_dbgmsg("autoit: bad file size - giving up\n");
return (det ? CL_VIRUS : CL_CLEAN);
}
if (!UNP.csize) {
cli_dbgmsg("autoit: skipping empty file\n");
base += 13 + 16;
continue;
}
cli_dbgmsg("autoit: compressed size: %x\n", UNP.csize);
cli_dbgmsg("autoit: advertised uncompressed size %x\n", cli_readint32(base + 5) ^ 0x45aa);
cli_dbgmsg("autoit: ref chksum: %x\n", cli_readint32(base + 9) ^ 0xc3d2);
base += 13 + 16;
if (cli_checklimits("autoit", ctx, UNP.csize, 0, 0) != CL_CLEAN) {
base += UNP.csize;
continue;
}
if (comp == 1 && UNP.csize < sizeof(union unaligned_32)) {
cli_dbgmsg("autoit: compressed size too small, skipping\n");
continue;
}
if (!(UNP.inputbuf = cli_malloc(UNP.csize)))
return CL_EMEM;
if (!fmap_need_ptr_once(map, base, UNP.csize)) {
cli_dbgmsg("autoit: failed to read compressed stream. broken/truncated file?\n");
free(UNP.inputbuf);
return (det ? CL_VIRUS : CL_CLEAN);
}
memcpy(UNP.inputbuf, base, UNP.csize);
base += UNP.csize;
MT_decrypt(UNP.inputbuf, UNP.csize, 0x22af + m4sum);
if (comp == 1) {
cli_dbgmsg("autoit: file is compressed\n");
if (cli_readint32(UNP.inputbuf) != 0x35304145) {
cli_dbgmsg("autoit: bad magic or unsupported version\n");
free(UNP.inputbuf);
continue;
}
if (!(UNP.usize = be32_to_host(*(uint32_t *)(UNP.inputbuf + 4))))
UNP.usize = UNP.csize; /* only a specifically crafted or badly corrupted sample should land here */
if (cli_checklimits("autoit", ctx, UNP.usize, 0, 0) != CL_CLEAN) {
free(UNP.inputbuf);
continue;
}
if (!(UNP.outputbuf = cli_malloc(UNP.usize))) {
free(UNP.inputbuf);
return CL_EMEM;
}
cli_dbgmsg("autoit: uncompressed size again: %x\n", UNP.usize);
UNP.cur_output = 0;
UNP.cur_input = 8;
UNP.bitmap.full = 0;
UNP.bits_avail = 0;
UNP.error = 0;
while (!UNP.error && UNP.cur_output < UNP.usize) {
if (getbits(&UNP, 1)) {
uint32_t bb, bs, addme = 0;
bb = getbits(&UNP, 15);
if ((bs = getbits(&UNP, 2)) == 3) {
addme = 3;
if ((bs = getbits(&UNP, 3)) == 7) {
addme = 10;
if ((bs = getbits(&UNP, 5)) == 31) {
addme = 41;
if ((bs = getbits(&UNP, 8)) == 255) {
addme = 296;
while ((bs = getbits(&UNP, 8)) == 255) {
addme += 255;
}
}
}
}
}
bs += 3 + addme;
/* If getbits set UNP.error, bail out here, since otherwise
* the data we'd write out would be garbage */
if (UNP.error) {
break;
}
if (!CLI_ISCONTAINED(UNP.outputbuf, UNP.usize, &UNP.outputbuf[UNP.cur_output], bs) ||
!CLI_ISCONTAINED(UNP.outputbuf, UNP.usize, &UNP.outputbuf[UNP.cur_output - bb], bs)) {
UNP.error = 1;
break;
}
while (bs--) {
UNP.outputbuf[UNP.cur_output] = UNP.outputbuf[UNP.cur_output - bb];
UNP.cur_output++;
}
} else {
UNP.outputbuf[UNP.cur_output] = (uint8_t)getbits(&UNP, 8);
UNP.cur_output++;
}
}
free(UNP.inputbuf);
/* Sometimes the autoit exe is in turn packed/lamed with a runtime compressor and similar shit.
* However, since the autoit script doesn't compress a second time very well, chances are we're
* still able to match the headers and unpack something (see sample 0811129)
* I'd rather unpack something (although possibly highly corrupted) than nothing at all
*
* - Fortuna audaces iuvat -
*/
if (UNP.error) {
cli_dbgmsg("autoit: decompression error after %u bytes - partial file may exist\n", UNP.cur_output);
UNP.usize = UNP.cur_output;
}
} else {
cli_dbgmsg("autoit: file is not compressed\n");
UNP.outputbuf = UNP.inputbuf;
UNP.usize = UNP.csize;
}
if (UNP.usize < 4) {
cli_dbgmsg("autoit: file is too short\n");
free(UNP.outputbuf);
continue;
}
files++;
/* FIXME: REGRESSION NEEDED! */
/* UNP.usize = u2a(UNP.outputbuf, UNP.usize); */
snprintf(tempfile, 1023, "%s" PATHSEP "autoit.%.3u", tmpd, files);
tempfile[1023] = '\0';
if ((i = open(tempfile, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR)) < 0) {
cli_dbgmsg("autoit: Can't create file %s\n", tempfile);
free(UNP.outputbuf);
return CL_ECREAT;
}
if (cli_writen(i, UNP.outputbuf, UNP.usize) != UNP.usize) {
cli_dbgmsg("autoit: cannot write %d bytes\n", UNP.usize);
close(i);
free(UNP.outputbuf);
return CL_EWRITE;
}
free(UNP.outputbuf);
if (ctx->engine->keeptmp)
cli_dbgmsg("autoit: file extracted to %s\n", tempfile);
else
cli_dbgmsg("autoit: file successfully extracted\n");
if (lseek(i, 0, SEEK_SET) == -1) {
cli_dbgmsg("autoit: call to lseek() has failed\n");
close(i);
return CL_ESEEK;
}
if (cli_magic_scan_desc(i, tempfile, ctx, NULL) == CL_VIRUS) {
if (!SCAN_ALLMATCHES) {
close(i);
if (!ctx->engine->keeptmp)
if (cli_unlink(tempfile)) return CL_EUNLINK;
return CL_VIRUS;
}
det = 1;
}
close(i);
if (!ctx->engine->keeptmp)
if (cli_unlink(tempfile)) return CL_EUNLINK;
}
return (det ? CL_VIRUS : ret);
}
/*********************
LAME realted stuff
*********************/
#define ROFL(a, b) ((a << (b % (sizeof(a) << 3))) | (a >> ((sizeof(a) << 3) - (b % (sizeof(a) << 3)))))
struct LAME {
uint32_t c0;
uint32_t c1;
uint32_t grp1[17];
};
static double LAME_fpusht(struct LAME *l)
{
union {
double as_double;
struct {
uint32_t lo;
uint32_t hi;
} as_uint;
} ret;
uint32_t rolled = ROFL(l->grp1[l->c0], 9) + ROFL(l->grp1[l->c1], 13);
l->grp1[l->c0] = rolled;
if (!l->c0--) l->c0 = 16;
if (!l->c1--) l->c1 = 16;
/* if (l->grp1[l->c0] == l->grp2[0]) { */
/* if (!memcmp(l->grp1, (uint32_t *)l + 0x24 - l->c0, 0x44)) */
/* return 0.0; */
/* } */
if (fpu_words == FPU_ENDIAN_LITTLE) {
ret.as_uint.lo = rolled << 0x14;
ret.as_uint.hi = 0x3ff00000 | (rolled >> 0xc);
} else {
ret.as_uint.hi = rolled << 0x14;
ret.as_uint.lo = 0x3ff00000 | (rolled >> 0xc);
}
return ret.as_double - 1.0;
}
static void LAME_srand(struct LAME *l, uint32_t seed)
{
unsigned int i;
for (i = 0; i < 17; i++) {
seed *= 0x53A9B4FB; /*1403630843*/
seed = 1 - seed;
l->grp1[i] = seed;
}
l->c0 = 0;
l->c1 = 10;
for (i = 0; i < 9; i++)
LAME_fpusht(l);
}
static uint8_t LAME_getnext(struct LAME *l)
{
double x;
uint8_t ret;
LAME_fpusht(l);
x = LAME_fpusht(l) * 256.0;
if ((int32_t)x < 256)
ret = (uint8_t)x;
else
ret = 0xff;
return ret;
}
static void LAME_decrypt(uint8_t *cypher, uint32_t size, uint16_t seed)
{
struct LAME lame;
/* mt_srand_timewrap(struct srand_struc bufDC); */
LAME_srand(&lame, (uint32_t)seed);
while (size--)
*cypher++ ^= LAME_getnext(&lame);
}
/*********************
autoit3 EA06 handler
*********************/
static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
{
uint8_t b[600], comp, script, *buf;
uint32_t s;
int i, ret, det = 0;
unsigned int files = 0;
char tempfile[1024];
const char prefixes[] = {'\0', '\0', '@', '$', '\0', '.', '"', '#'};
const char *opers[] = {",", "=", ">", "<", "<>", ">=", "<=", "(", ")", "+", "-", "/", "*", "&", "[", "]", "==", "^", "+=", "-=", "/=", "*=", "&=", "?", ":"};
struct UNP UNP;
fmap_t *map = ctx->fmap;
/* Useless due to a bug in CRC calculation - LMAO!!1 */
/* if (cli_readn(desc, buf, 24)!=24) */
/* return CL_CLEAN; */
/* LAME_decrypt(buf, 0x10, 0x99f2); */
/* buf+=0x10; */
base += 16; /* for now we just skip the garbage */
while ((ret = cli_checklimits("cli_autoit", ctx, 0, 0, 0)) == CL_CLEAN) {
if (!fmap_need_ptr_once(map, base, 8))
return (det ? CL_VIRUS : CL_CLEAN);
/* LAME_decrypt(buf, 4, 0x18ee); waste of time */
if (cli_readint32(base) != 0x52ca436b) {
cli_dbgmsg("autoit: no FILE magic found, giving up (got 0x%08x)\n", cli_readint32(base));
return (det ? CL_VIRUS : CL_CLEAN);
}
script = 0;
s = cli_readint32(base + 4) ^ 0xadbc;
if ((int32_t)(s * 2) < 0)
return (det ? CL_VIRUS : CL_CLEAN); /* the original code wouldn't seek back here */
base += 8;
if (s < sizeof(b) / 2) {
if (!fmap_need_ptr_once(map, base, s * 2))
return (det ? CL_VIRUS : CL_CLEAN);
memcpy(b, base, s * 2);
LAME_decrypt(b, s * 2, s + 0xb33f);
u2a(b, s * 2);
cli_dbgmsg("autoit: magic string '%s'\n", b);
if (s == 19 && !memcmp(">>>AUTOIT SCRIPT<<<", b, 19))
script = 1;
} else {
cli_dbgmsg("autoit: magic string too long to print\n");
}
base += s * 2;
if (!fmap_need_ptr_once(map, base, 4))
return (det ? CL_VIRUS : CL_CLEAN);
s = cli_readint32(base) ^ 0xf820;
if ((int32_t)(s * 2) < 0)
return (det ? CL_VIRUS : CL_CLEAN); /* the original code wouldn't seek back here */
base += 4;
if (cli_debug_flag && s < sizeof(b) / 2) {
if (!fmap_need_ptr_once(map, base, s * 2))
return (det ? CL_VIRUS : CL_CLEAN);
memcpy(b, base, s * 2);
LAME_decrypt(b, s * 2, s + 0xf479);
b[s * 2] = '\0';
b[s * 2 + 1] = '\0';
u2a(b, s * 2);
cli_dbgmsg("autoit: original filename '%s'\n", b);
}
base += s * 2;
if (!fmap_need_ptr_once(map, base, 13))
return (det ? CL_VIRUS : CL_CLEAN);
comp = *base;
UNP.csize = cli_readint32(base + 1) ^ 0x87bc;
if ((int32_t)UNP.csize < 0) {
cli_dbgmsg("autoit: bad file size - giving up\n");
return (det ? CL_VIRUS : CL_CLEAN);
}
if (!UNP.csize) {
cli_dbgmsg("autoit: skipping empty file\n");
base += 13 + 16;
continue;
}
cli_dbgmsg("autoit: compressed size: %x\n", UNP.csize);
cli_dbgmsg("autoit: advertised uncompressed size %x\n", cli_readint32(base + 5) ^ 0x87bc);
cli_dbgmsg("autoit: ref chksum: %x\n", cli_readint32(base + 9) ^ 0xa685);
base += 13 + 16;
if (cli_checklimits("autoit", ctx, UNP.csize, 0, 0) != CL_CLEAN) {
base += UNP.csize;
continue;
}
if (comp == 1 && UNP.csize < sizeof(union unaligned_32)) {
cli_dbgmsg("autoit: compressed size too small, skipping\n");
continue;
}
files++;
if (!(UNP.inputbuf = cli_malloc(UNP.csize)))
return CL_EMEM;
if (!fmap_need_ptr_once(map, base, UNP.csize)) {
cli_dbgmsg("autoit: failed to read compressed stream. broken/truncated file?\n");
free(UNP.inputbuf);
return (det ? CL_VIRUS : CL_CLEAN);
}
memcpy(UNP.inputbuf, base, UNP.csize);
base += UNP.csize;
LAME_decrypt(UNP.inputbuf, UNP.csize, 0x2477 /* + m4sum (broken by design) */);
if (comp == 1) {
cli_dbgmsg("autoit: file is compressed\n");
if (cli_readint32(UNP.inputbuf) != 0x36304145) {
cli_dbgmsg("autoit: bad magic or unsupported version\n");
free(UNP.inputbuf);
continue;
}
if (!(UNP.usize = be32_to_host(*(uint32_t *)(UNP.inputbuf + 4))))
UNP.usize = UNP.csize; /* only a specifically crafted or badly corrupted sample should land here */
if (cli_checklimits("autoit", ctx, UNP.usize, 0, 0) != CL_CLEAN) {
free(UNP.inputbuf);
continue;
}
if (!(UNP.outputbuf = cli_malloc(UNP.usize))) {
free(UNP.inputbuf);
return CL_EMEM;
}
cli_dbgmsg("autoit: uncompressed size again: %x\n", UNP.usize);
UNP.cur_output = 0;
UNP.cur_input = 8;
UNP.bitmap.full = 0;
UNP.bits_avail = 0;
UNP.error = 0;
while (!UNP.error && UNP.cur_output < UNP.usize) {
if (!getbits(&UNP, 1)) {
uint32_t bb, bs, addme = 0;
bb = getbits(&UNP, 15);
if ((bs = getbits(&UNP, 2)) == 3) {
addme = 3;
if ((bs = getbits(&UNP, 3)) == 7) {
addme = 10;
if ((bs = getbits(&UNP, 5)) == 31) {
addme = 41;
if ((bs = getbits(&UNP, 8)) == 255) {
addme = 296;
while ((bs = getbits(&UNP, 8)) == 255) {
addme += 255;
}
}
}
}
}
bs += 3 + addme;
/* If getbits set UNP.error, bail out here, since otherwise
* the data we'd write out would be garbage */
if (UNP.error) {
break;
}
//cli_dbgmsg("cur_output: %u, bs: %u, bb: %u\n", UNP.cur_output, bs, bb);
if (!CLI_ISCONTAINED(UNP.outputbuf, UNP.usize, &UNP.outputbuf[UNP.cur_output], bs) ||
!CLI_ISCONTAINED(UNP.outputbuf, UNP.usize, &UNP.outputbuf[UNP.cur_output - bb], bs)) {
UNP.error = 1;
break;
}
while (bs--) {
UNP.outputbuf[UNP.cur_output] = UNP.outputbuf[UNP.cur_output - bb];
UNP.cur_output++;
}
} else {
UNP.outputbuf[UNP.cur_output] = (uint8_t)getbits(&UNP, 8);
UNP.cur_output++;
}
}
free(UNP.inputbuf);
if (UNP.error) {
cli_dbgmsg("autoit: decompression error after %u bytes - partial file may exist\n", UNP.cur_output);
UNP.usize = UNP.cur_output;
}
} else {
cli_dbgmsg("autoit: file is not compressed\n");
UNP.outputbuf = UNP.inputbuf;
UNP.usize = UNP.csize;
}
if (UNP.usize < 4) {
cli_dbgmsg("autoit: file is too short\n");
free(UNP.outputbuf);
continue;
}
if (script) {
/* From here on, we'll reuse csize to be the size of the
* output buffer */
UNP.csize = UNP.usize;
if (!(buf = cli_malloc(UNP.csize))) {
free(UNP.outputbuf);
return CL_EMEM;
}
UNP.cur_output = 0;
UNP.cur_input = 4;
UNP.bits_avail = cli_readint32((char *)UNP.outputbuf);
UNP.error = 0;
cli_dbgmsg("autoit: script has got %u lines\n", UNP.bits_avail);
while (!UNP.error && UNP.bits_avail && UNP.cur_input < UNP.usize) {
uint8_t op;
switch ((op = UNP.outputbuf[UNP.cur_input++])) {
case 0: /* keyword ID */ {
uint32_t keyword_id;
uint32_t keyword_len;
if (UNP.cur_input >= UNP.usize - 4) {
UNP.error = 1;
cli_dbgmsg("autoit: too few bytes present - expected enough for a keyword ID\n");
break;
}
keyword_id = cli_readint32((char *)&UNP.outputbuf[UNP.cur_input]);
if (keyword_id >= (sizeof(autoit_keywords) / sizeof(autoit_keywords[0]))) {
UNP.error = 1;
cli_dbgmsg("autoit: unknown AutoIT keyword ID: 0x%x\n", keyword_id);
break;
}
UNP.cur_input += 4;
keyword_len = strlen(autoit_keywords[keyword_id]);
if (UNP.cur_output + keyword_len + 2 >= UNP.csize) {
uint8_t *newout;
UNP.csize += 512;
if (!(newout = cli_realloc(buf, UNP.csize))) {
UNP.error = 1;
break;
}
buf = newout;
}
if (cli_debug_flag) {
if (0 == memcmp(autoit_keywords[keyword_id], "UNKNOWN", MIN(strlen("UNKNOWN"), keyword_len))) {
cli_dbgmsg("autoit: encountered use of unknown keyword ID: %s\n", autoit_keywords[keyword_id]);
}
}
snprintf((char *)&buf[UNP.cur_output], keyword_len + 2, "%s ", autoit_keywords[keyword_id]);
UNP.cur_output += keyword_len + 1;
break;
}
case 1: /* function ID */ {
uint32_t function_id;
uint32_t function_len;
if (UNP.cur_input >= UNP.usize - 4) {
UNP.error = 1;
cli_dbgmsg("autoit: too few bytes present - expected enough for a function ID\n");
break;
}
function_id = cli_readint32((char *)&UNP.outputbuf[UNP.cur_input]);
if (function_id >= (sizeof(autoit_functions) / sizeof(autoit_functions[0]))) {
UNP.error = 1;
cli_dbgmsg("autoit: unknown AutoIT function ID: 0x%x\n", function_id);
break;
}
UNP.cur_input += 4;
function_len = strlen(autoit_functions[function_id]);
if (UNP.cur_output + function_len + 2 >= UNP.csize) {
uint8_t *newout;
UNP.csize += 512;
if (!(newout = cli_realloc(buf, UNP.csize))) {
UNP.error = 1;
break;
}
buf = newout;
}
if (cli_debug_flag) {
if (0 == memcmp(autoit_functions[function_id], "UNKNOWN", MIN(strlen("UNKNOWN"), function_len))) {
cli_dbgmsg("autoit: encountered use of unknown function ID: %s\n", autoit_functions[function_id]);
}
}
snprintf((char *)&buf[UNP.cur_output], function_len + 2, "%s ", autoit_functions[function_id]);
UNP.cur_output += function_len + 1;
break;
}
case 5: /* <INT> */
if (UNP.cur_input >= UNP.usize - 4) {
UNP.error = 1;
cli_dbgmsg("autoit: not enough space for an int\n");
break;
}
if (UNP.cur_output + 12 >= UNP.csize) {
uint8_t *newout;
UNP.csize += 512;
if (!(newout = cli_realloc(buf, UNP.csize))) {
UNP.error = 1;
break;
}
buf = newout;
}
snprintf((char *)&buf[UNP.cur_output], 12, "0x%08x ", cli_readint32((char *)&UNP.outputbuf[UNP.cur_input]));
UNP.cur_output += 11;
UNP.cur_input += 4;
break;
case 0x10: /* <INT64> */
{
uint64_t val;
if (UNP.usize < 8 || UNP.cur_input >= UNP.usize - 8) {
UNP.error = 1;
cli_dbgmsg("autoit: not enough space for an int64\n");
break;
}
if (UNP.cur_output + 20 >= UNP.csize) {
uint8_t *newout;
UNP.csize += 512;
if (!(newout = cli_realloc(buf, UNP.csize))) {
UNP.error = 1;
break;
}
buf = newout;
}
val = (uint64_t)cli_readint32((char *)&UNP.outputbuf[UNP.cur_input + 4]);
val <<= 32;
val += (uint64_t)cli_readint32((char *)&UNP.outputbuf[UNP.cur_input]);
snprintf((char *)&buf[UNP.cur_output], 20, "0x%016lx ", (unsigned long int)val);
UNP.cur_output += 19;
UNP.cur_input += 8;
break;
}
case 0x20: /* <DOUBLE> */
if (UNP.usize < 8 || UNP.cur_input >= UNP.usize - 8) {
UNP.error = 1;
cli_dbgmsg("autoit: not enough space for a double\n");
break;
}
if (UNP.cur_output + 40 >= UNP.csize) {
uint8_t *newout;
UNP.csize += 512;
if (!(newout = cli_realloc(buf, UNP.csize))) {
UNP.error = 1;
break;
}
buf = newout;
}
if (fpu_words == FPU_ENDIAN_LITTLE)
snprintf((char *)&buf[UNP.cur_output], 39, "%g ", *(double *)&UNP.outputbuf[UNP.cur_input]);
else
do {
double x;
uint8_t *j = (uint8_t *)&x;
unsigned int i;
for (i = 0; i < 8; i++)
j[7 - i] = UNP.outputbuf[UNP.cur_input + i];
snprintf((char *)&buf[UNP.cur_output], 39, "%g ", x); /* FIXME: check */
} while (0);
buf[UNP.cur_output + 38] = ' ';
buf[UNP.cur_output + 39] = '\0';
UNP.cur_output += strlen((char *)&buf[UNP.cur_output]);
UNP.cur_input += 8;
break;
case 0x30: /* COSTRUCT */
case 0x31: /* COMMAND */
case 0x32: /* MACRO */
case 0x33: /* VAR */
case 0x34: /* FUNC */
case 0x35: /* OBJECT */
case 0x36: /* STRING */
case 0x37: /* DIRECTIVE */
{
uint32_t chars, dchars, i;
if (UNP.cur_input >= UNP.usize - 4) {
UNP.error = 1;
cli_dbgmsg("autoit: not enough space for size\n");
break;
}
chars = cli_readint32((char *)&UNP.outputbuf[UNP.cur_input]);
dchars = chars * 2;
UNP.cur_input += 4;
if (UNP.usize < dchars || UNP.cur_input >= UNP.usize - dchars) {
UNP.error = 1;
cli_dbgmsg("autoit: size too big - needed %d, total %d, avail %d\n", dchars, UNP.usize, UNP.usize - UNP.cur_input);
break;
}
if (UNP.cur_output + chars + 3 >= UNP.csize) {
uint8_t *newout;
UNP.csize += chars + 512;
if (!(newout = cli_realloc(buf, UNP.csize))) {
UNP.error = 1;
break;
}
buf = newout;
}
if (prefixes[op - 0x30])
buf[UNP.cur_output++] = prefixes[op - 0x30];
if (chars) {
for (i = 0; i < dchars; i += 2) {
UNP.outputbuf[UNP.cur_input + i] ^= (uint8_t)chars;
UNP.outputbuf[UNP.cur_input + i + 1] ^= (uint8_t)(chars >> 8);
}
u2a(&UNP.outputbuf[UNP.cur_input], dchars);
memcpy(&buf[UNP.cur_output], &UNP.outputbuf[UNP.cur_input], chars);
UNP.cur_output += chars;
UNP.cur_input += dchars;
}
if (op == 0x36)
buf[UNP.cur_output++] = '"';
if (op != 0x34)
buf[UNP.cur_output++] = ' ';
} break;
case 0x40: /* , */
case 0x41: /* = */
case 0x42: /* > */
case 0x43: /* < */
case 0x44: /* <> */
case 0x45: /* >= */
case 0x46: /* <= */
case 0x47: /* ( */
case 0x48: /* ) */
case 0x49: /* + */
case 0x4a: /* - */
case 0x4b: /* / */
case 0x4c: /* * */
case 0x4d: /* & */
case 0x4e: /* [ */
case 0x4f: /* ] */
case 0x50: /* == */
case 0x51: /* ^ */
case 0x52: /* += */
case 0x53: /* -= */
case 0x54: /* /= */
case 0x55: /* *= */
case 0x56: /* &= */
case 0x57: /* ? */
case 0x58: /* : */
if (UNP.cur_output + 4 >= UNP.csize) {
uint8_t *newout;
UNP.csize += 512;
if (!(newout = cli_realloc(buf, UNP.csize))) {
UNP.error = 1;
break;
}
buf = newout;
}
UNP.cur_output += snprintf((char *)&buf[UNP.cur_output], 4, "%s ", opers[op - 0x40]);
break;
case 0x7f:
UNP.bits_avail--;
if (UNP.cur_output + 1 >= UNP.csize) {
uint8_t *newout;
UNP.csize += 512;
if (!(newout = cli_realloc(buf, UNP.csize))) {
UNP.error = 1;
break;
}
buf = newout;
}
buf[UNP.cur_output++] = '\n';
break;
default:
cli_dbgmsg("autoit: found unknown op (0x%x)\n", op);
UNP.error = 1;
}
}
if (UNP.error)
cli_dbgmsg("autoit: decompilation aborted - partial script may exist\n");
free(UNP.outputbuf);
} else {
buf = UNP.outputbuf;
UNP.cur_output = UNP.usize;
}
snprintf(tempfile, 1023, "%s" PATHSEP "autoit.%.3u", tmpd, files);
tempfile[1023] = '\0';
if ((i = open(tempfile, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR)) < 0) {
cli_dbgmsg("autoit: Can't create file %s\n", tempfile);
free(buf);
return CL_ECREAT;
}
if (cli_writen(i, buf, UNP.cur_output) != UNP.cur_output) {
cli_dbgmsg("autoit: cannot write %d bytes\n", UNP.usize);
close(i);
free(buf);
return CL_EWRITE;
}
free(buf);
if (ctx->engine->keeptmp)
cli_dbgmsg("autoit: %s extracted to %s\n", (script) ? "script" : "file", tempfile);
else
cli_dbgmsg("autoit: %s successfully extracted\n", (script) ? "script" : "file");
if (lseek(i, 0, SEEK_SET) == -1) {
cli_dbgmsg("autoit: call to lseek() has failed\n");
close(i);
return CL_ESEEK;
}
if (cli_magic_scan_desc(i, tempfile, ctx, NULL) == CL_VIRUS) {
if (!SCAN_ALLMATCHES) {
close(i);
if (!ctx->engine->keeptmp)
if (cli_unlink(tempfile)) return CL_EUNLINK;
return CL_VIRUS;
}
det = 1;
}
close(i);
if (!ctx->engine->keeptmp)
if (cli_unlink(tempfile)) return CL_EUNLINK;
}
return (det ? CL_VIRUS : ret);
}
/*********************
autoit3 wrapper
*********************/
int cli_scanautoit(cli_ctx *ctx, off_t offset)
{
const uint8_t *version;
int r;
char *tmpd;
fmap_t *map = ctx->fmap;
cli_dbgmsg("in scanautoit()\n");
if (!(version = fmap_need_off_once(map, offset, sizeof(*version))))
return CL_EREAD;
if (!(tmpd = cli_gentemp_with_prefix(ctx->sub_tmpdir, "autoit-tmp")))
return CL_ETMPDIR;
if (mkdir(tmpd, 0700)) {
cli_dbgmsg("autoit: Can't create temporary directory %s\n", tmpd);
free(tmpd);
return CL_ETMPDIR;
}
if (ctx->engine->keeptmp)
cli_dbgmsg("autoit: Extracting files to %s\n", tmpd);
switch (*version) {
case 0x35:
r = ea05(ctx, version + 1, tmpd);
break;
case 0x36:
if (fpu_words == FPU_ENDIAN_INITME)
fpu_words = get_fpu_endian();
if (fpu_words == FPU_ENDIAN_UNKNOWN) {
cli_dbgmsg("autoit: EA06 support not available"
"(cannot extract ea06 doubles, unknown floating double representation).\n");
r = CL_CLEAN;
} else
r = ea06(ctx, version + 1, tmpd);
break;
default:
/* NOT REACHED */
cli_dbgmsg("autoit: unknown method\n");
r = CL_CLEAN;
}
if (!ctx->engine->keeptmp)
cli_rmdirs(tmpd);
free(tmpd);
return r;
}