Clam Antivirus - wrapping the libclamav.dll

Just starting out? Need help? Post your questions and find answers here.
thanos
Enthusiast
Enthusiast
Posts: 422
Joined: Sat Jan 12, 2008 3:25 pm
Location: Greece
Contact:

Clam Antivirus - wrapping the libclamav.dll

Post by thanos »

Hello.
I try to write a wrapper for a simple offline av using the ClamAV which is an open source antivirus engine.
I tried with the help of PureDLLHelper but I am not experienced in C++ and I am not familiar with its functions and types.
As I saw the needed functions are:

Code: Select all

 int cl_init(unsigned int options);
    struct cl_engine *cl_engine_new(void);
    int cl_engine_free(struct cl_engine *engine);
    
     const char *cl_retdbdir(void);

    int cl_load(const char *path, struct cl_engine *engine,
            unsigned int *signo, unsigned int options);

 int cl_engine_compile(struct cl_engine *engine);
 
     int cl_scanfile(
        const char *filename,
        const char **virname,
        unsigned long int *scanned,
        const struct cl_engine *engine,
        struct cl_scan_options *options);
The doc in html format is https://www.clamav.net/documents/libclamav

My not so successfull attempt:

Code: Select all

;==========================================================================
; Generated with PureDLLHelper, Copyright ©2012 by Thomas <ts-soft> Schulz
;==========================================================================

PrototypeC cl_engine_compile(engine)
PrototypeC cl_engine_free()
PrototypeC cl_engine_new()
PrototypeC cl_init(options)
PrototypeC cl_load(*path, *engine)
PrototypeC cl_scanfile(*file, *virname, scanned, *engine, option)
 
Global cl_engine_compile.cl_engine_compile
Global cl_engine_free.cl_engine_free
Global cl_engine_new.cl_engine_new
Global cl_init.cl_init
Global cl_load.cl_load
Global cl_scanfile.cl_scanfile

Procedure.i libclamav_LoadDLL()
  Protected hDLL.i

  hDLL = OpenLibrary(#PB_Any, "libclamav.dll")
  If hDLL <> 0
    cl_engine_compile = GetFunction(hDLL, "cl_engine_compile")
    cl_engine_free = GetFunction(hDLL, "cl_engine_free")
    cl_engine_new = GetFunction(hDLL, "cl_engine_new")
    cl_init = GetFunction(hDLL, "cl_init")
    cl_load = GetFunction(hDLL, "cl_load")
    cl_scanfile = GetFunction(hDLL, "cl_scanfile")

    ProcedureReturn hDLL
  EndIf

  ProcedureReturn #False
EndProcedure

#CL_INIT_DEFAULT = 0

hLib = libclamav_LoadDLL()
Debug hLib ; => 8259840

hInit = cl_init(#CL_INIT_DEFAULT)
Debug hInit ; => 0

*Engn = cl_engine_new()
Debug *Engn ; => 57833072

hDB = cl_load(@"C:/Programs/ClamWin/bin/database/main.cvd", *Engn)
Debug hDB ; => 11

hComp = cl_engine_compile(*Engn)
Debug hComp ; => 0

*virname = 0

retv = cl_scanfile(@"C:/Programs/ClamWin/bin/libclamav_llvm.dll", *virname, #NUL, *g, #NUL)
Debug retv ; => 8
Can anyone help with conversions?
» myPersonal Banker :: Because you do not need to have a master degree in economics in order to organize your finances!
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: Clam Antivirus - wrapping the libclamav.dll

Post by Keya »

just throwing out a wild guess here but make sure you're using either ascii or unicode strings - whatever Clam is using
GPI
PureBasic Expert
PureBasic Expert
Posts: 1394
Joined: Fri Apr 25, 2003 6:41 pm

Re: Clam Antivirus - wrapping the libclamav.dll

Post by GPI »

Some notes:
unsigned int, signed int and int are always 32-bit-values, even on a 64-bit exe. So you should always use "l"/"long".
a "long int" is a 64-bit value, use "q" or "quad"

"a const char *path" can you replace in a prototype with "path.p-utf8" (note no star here!) - pb will then convert all strings to a utf8. You need utf8 to support special chars in the directory-system.

when a procedure return "const char *" you should save this return in a pointer (*return). to get the string use Peeks(*return, -1, #pb_utf8)

const char **virname is a pointer to pointer.
you should use something like this

Code: Select all

*pointer = utf8("mystring)
blablabla...(...., @*pointer)
freemememory(*pointer)
you should read the manual, maybe you need something like pointer-array and every entry should point to a utf8-string, maybe the last element should be #null

*OR* the routine whant you give back a pointer to a string.
in this case

Code: Select all

protected *returnstring
blablabla...(, @ *returnstring)
debug peeks(*returnstring,-1,#pb_utf8)
unsigned long int *scanned - means he want a pointer to a long. it seems it will want to write to this.

Code: Select all

proected Scanned.l
blablabal..(@scanned)
debug scanned
"struct cl_engine *engine" is a pointer to a structure cl_engine. When you don't need to access the members of the structure, you can simple use a pointer to void (*engine) without type. It seems that the Routines use this as a kind of handle. so replace it simple with "*engine"
thanos
Enthusiast
Enthusiast
Posts: 422
Joined: Sat Jan 12, 2008 3:25 pm
Location: Greece
Contact:

Re: Clam Antivirus - wrapping the libclamav.dll

Post by thanos »

@GPI
Thank you very much!
The things became clearer :D
I will try to correct my code
» myPersonal Banker :: Because you do not need to have a master degree in economics in order to organize your finances!
thanos
Enthusiast
Enthusiast
Posts: 422
Joined: Sat Jan 12, 2008 3:25 pm
Location: Greece
Contact:

Re: Clam Antivirus - wrapping the libclamav.dll

Post by thanos »

After GPI's valuable corrections I edit the code and now it seems to work.
The good thing is that loads the virus database 25% faster than the ClamAV itself.

Code: Select all

EnableExplicit

;~ int cl_engine_compile(struct cl_engine *engine);
PrototypeC cl_engine_compile(*engine)

;~ int cl_load(const char *path, struct cl_engine *engine, unsigned int *signo, unsigned int options)
PrototypeC cl_load(path.p-utf8, *engine, *signo, options.l)

PrototypeC cl_engine_free()

PrototypeC cl_engine_new()

;~ int cl_init(unsigned int options);
PrototypeC cl_init(options.l)

;~ int cl_scanfile(const char *filename, const char **virname,unsigned long INT *scanned, const struct cl_engine *engine,unsigned INT options);
PrototypeC cl_scanfile(file.p-utf8, *virname, scanned.l, *engine, option.l)

Global cl_engine_compile.cl_engine_compile
Global cl_engine_free.cl_engine_free
Global cl_engine_new.cl_engine_new
Global cl_init.cl_init
Global cl_load.cl_load
Global cl_scanfile.cl_scanfile


Procedure.i libclamav_LoadDLL()
  Protected hDLL.i
  
  hDLL = OpenLibrary(#PB_Any, "libclamav.dll")
  If hDLL <> 0
    cl_engine_compile = GetFunction(hDLL, "cl_engine_compile")
    cl_engine_free = GetFunction(hDLL, "cl_engine_free")
    cl_engine_new = GetFunction(hDLL, "cl_engine_new")
    cl_init = GetFunction(hDLL, "cl_init")
    cl_load = GetFunction(hDLL, "cl_load")
    cl_scanfile = GetFunction(hDLL, "cl_scanfile")
    
    ProcedureReturn hDLL
  EndIf
  
  ProcedureReturn #False
EndProcedure

#CL_INIT_DEFAULT = 0

Define hLib = libclamav_LoadDLL()
Debug "hLib: " + Str(hLib)

Define hInit = cl_init(#CL_INIT_DEFAULT)
Debug "hInit: " + Str(hInit)

Define *Engn = cl_engine_new()
Debug "*Engn: " + Str(*Engn)
Define hDB = cl_load("C:/Programs/ClamWin/bin/database/main.cvd", *Engn, @"nothing", 0)
Debug "hDB: " + Str(hDB)

Define hComp = cl_engine_compile(*Engn)
Debug "hComp: " + Str(hComp)

;g =  cl_scanfile(char *filename, const char **virname,unsigned long INT *scanned, const struct cl_engine *engine,unsigned INT options);//Incoming file path; virus engine, perform scan, return value: CL_VIRUS means there is virus; CL_CLEAN means no virus;
Define *virname = UTF8("Some Virus")
Define scanned.l = 0
Define *options = UTF8("0")
Debug "Scan file"
Define retv = cl_scanfile("C:/Program Files (x86)/openElement/openElement 1.53 R5/openElement.exe", @*virname, scanned, *Engn, @*options)
FreeMemory(*virname)
Debug retv
» myPersonal Banker :: Because you do not need to have a master degree in economics in order to organize your finances!
vwidmer
Enthusiast
Enthusiast
Posts: 282
Joined: Mon Jan 20, 2014 6:32 pm

Re: Clam Antivirus - wrapping the libclamav.dll

Post by vwidmer »

I am running on linux with the code below it seems to work if I comment out the FreeMemory any idea why that is?

Code: Select all

EnableExplicit

;~ int cl_engine_compile(struct cl_engine *engine);
PrototypeC cl_engine_compile(*engine)

;~ int cl_load(const char *path, struct cl_engine *engine, unsigned int *signo, unsigned int options)
PrototypeC cl_load(path.p-utf8, *engine, *signo, options.l)

PrototypeC cl_engine_free()

PrototypeC cl_engine_new()

;~ int cl_init(unsigned int options);
PrototypeC cl_init(options.l)

;~ int cl_scanfile(const char *filename, const char **virname,unsigned long INT *scanned, const struct cl_engine *engine,unsigned INT options);
PrototypeC cl_scanfile(file.p-utf8, *virname, scanned.l, *engine, option.l)

Global cl_engine_compile.cl_engine_compile
Global cl_engine_free.cl_engine_free
Global cl_engine_new.cl_engine_new
Global cl_init.cl_init
Global cl_load.cl_load
Global cl_scanfile.cl_scanfile


Procedure.i libclamav_LoadDLL()
  Protected hDLL.i
  
  hDLL = OpenLibrary(#PB_Any, "/usr/lib/libclamav.so")
  If hDLL <> 0
    cl_engine_compile = GetFunction(hDLL, "cl_engine_compile")
    cl_engine_free = GetFunction(hDLL, "cl_engine_free")
    cl_engine_new = GetFunction(hDLL, "cl_engine_new")
    cl_init = GetFunction(hDLL, "cl_init")
    cl_load = GetFunction(hDLL, "cl_load")
    cl_scanfile = GetFunction(hDLL, "cl_scanfile")
    
    ProcedureReturn hDLL
  EndIf
  
  ProcedureReturn #False
EndProcedure

#CL_INIT_DEFAULT = 0

Define hLib = libclamav_LoadDLL()
Debug "hLib: " + Str(hLib)

Define hInit = cl_init(#CL_INIT_DEFAULT)
Debug "hInit: " + Str(hInit)

Define *Engn = cl_engine_new()
Debug "*Engn: " + Str(*Engn)
Define hDB = cl_load("/var/lib/clamav/main.cvd", *Engn, @"nothing", 0)
Debug "hDB: " + Str(hDB)

Define hComp = cl_engine_compile(*Engn)
Debug "hComp: " + Str(hComp)

;g =  cl_scanfile(char *filename, const char **virname,unsigned long INT *scanned, const struct cl_engine *engine,unsigned INT options);//Incoming file path; virus engine, perform scan, return value: CL_VIRUS means there is virus; CL_CLEAN means no virus;
Define *virname = UTF8("Some Virus")
Define scanned.l = 0
Define *options = UTF8("0")
Debug "Scan file"
Define retv = cl_scanfile("/home/user/Downloads/eicar.com", @*virname, scanned, *Engn, @*options)
;;FreeMemory(*virname)
Debug retv

[17:18:31] Waiting for executable to start...
[17:18:31] Executable type: Linux - x64 (64bit, Unicode, Thread, Purifier)
[17:18:32] Executable started.
[17:18:37] [ERROR] clamav-wrapper.pb (Line: 67)
[17:18:37] [ERROR] Trying to free or to reallocate a non-allocated memory block
[17:18:45] The Program was killed.
WARNING: I dont know what I am doing! I just put stuff here and there and sometimes like magic it works. So please improve on my code and post your changes so I can learn more. TIA
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: Clam Antivirus - wrapping the libclamav.dll

Post by Keya »

vwidmer wrote: Fri Jun 25, 2021 10:22 pm it seems to work if I comment out the FreeMemory any idea why that is?

Code: Select all

Define *virname = UTF8("Some Virus")
;;FreeMemory(*virname)
I think FreeMemory is only meant to be used after a call like AllocateMemory ... I don't think it's meant to be used after a call to string functions like UTF8()
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Clam Antivirus - wrapping the libclamav.dll

Post by infratec »

No, it works also with UTF8(), but ...

I think the memory is modified/extended by the clamav procedure.
Which results in an error at FreeMemory()

Maybe if the buffer is large enough, the problem does not appear:

Code: Select all

*virname = AllocateMemory(1024)
If *virname
  retv = cl_scanfile("/home/user/Downloads/eicar.com", @*virname, scanned, *Engn, @*options)
  Debug PeekS(*virname, -1, #PB_UTF8)
  FreeMemory(*virname)
EndIf
Or ClamAV allocates the memory itself:

Code: Select all

Define *virname

retv = cl_scanfile("/home/user/Downloads/eicar.com", @*virname, scanned, *Engn, @*options)
If *virname
  Debug PeekS(*virname, -1, #PB_UTF8)
EndIf
I think it is the last version, because else it makes no sense to use a pointer to a pointer.
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Clam Antivirus - wrapping the libclamav.dll

Post by infratec »

This works in windows and linux, maybe in OSX too:

Code: Select all

;
; Binary versions:
;
; https://www.clamav.net/downloads#otherversions
;
; for windows you need to extract the following dlls:
;
; json-c.dll
; libbz2.dll
; libclamav.dll
; libcrypto-1_1.dll
; libmspack.dll
; libxml2.dll
; pcre2-8.dll
; pthreadVC2.dll
;
; the Clamav Virus Definition files:
;
; http://database.clamav.net/main.cvd
; http://database.clamav.net/daily.cvd
;

CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
CompilerEndIf

#CL_INIT_DEFAULT = 0

Enumeration
  ; libclamav specific
  #CL_CLEAN = 0
  #CL_SUCCESS = 0
  #CL_VIRUS
  #CL_ENULLARG
  #CL_EARG
  #CL_EMALFDB
  #CL_ECVD
  #CL_EVERIFY
  #CL_EUNPACK
  
  ; I/O And memory errors
  #CL_EOPEN
  #CL_ECREAT
  #CL_EUNLINK
  #CL_ESTAT
  #CL_EREAD
  #CL_ESEEK
  #CL_EWRITE
  #CL_EDUP
  #CL_EACCES
  #CL_ETMPFILE
  #CL_ETMPDIR
  #CL_EMAP
  #CL_EMEM
  #CL_ETIMEOUT
  
  ; internal (Not reported outside libclamav)
  #CL_BREAK
  #CL_EMAXREC
  #CL_EMAXSIZE
  #CL_EMAXFILES
  #CL_EFORMAT
  #CL_EPARSE
  #CL_EBYTECODE ; may be reported in testmode
  #CL_EBYTECODE_TESTFAIL ; may be reported in testmode
  
  ; c4w error codes
  #CL_ELOCK
  #CL_EBUSY
  #CL_ESTATE
  
  ; no error codes below this line please
  #CL_ELAST_ERROR
EndEnumeration


; db options
#CL_DB_PHISHING	          = $000002
#CL_DB_PHISHING_URLS      = $000008
#CL_DB_PUA	              = $000010
#CL_DB_CVDNOTMP	          = $000020  ; obsolete
#CL_DB_OFFICIAL	          = $000040  ; internal
#CL_DB_PUA_MODE	          = $000080
#CL_DB_PUA_INCLUDE        = $000100
#CL_DB_PUA_EXCLUDE        = $000200
#CL_DB_COMPILED	          = $000400  ; internal
#CL_DB_DIRECTORY          = $000800  ; internal
#CL_DB_OFFICIAL_ONLY      = $001000
#CL_DB_BYTECODE           = $002000
#CL_DB_SIGNED             = $004000  ; internal
#CL_DB_BYTECODE_UNSIGNED  = $008000
#CL_DB_UNSIGNED	          = $010000  ; internal
#CL_DB_BYTECODE_STATS     = $020000
#CL_DB_ENHANCED           = $040000
#CL_DB_PCRE_STATS         = $080000
#CL_DB_YARA_EXCLUDE       = $100000
#CL_DB_YARA_ONLY          = $200000

; recommended db settings
#CL_DB_STDOPT = (#CL_DB_PHISHING | #CL_DB_PHISHING_URLS | #CL_DB_BYTECODE)




Enumeration cl_engine_field
  #CL_ENGINE_MAX_SCANSIZE         ; uint64_t
  #CL_ENGINE_MAX_FILESIZE         ; uint64_t
  #CL_ENGINE_MAX_RECURSION        ; uint32_t
  #CL_ENGINE_MAX_FILES            ; uint32_t
  #CL_ENGINE_MIN_CC_COUNT         ; uint32_t
  #CL_ENGINE_MIN_SSN_COUNT        ; uint32_t
  #CL_ENGINE_PUA_CATEGORIES       ; (char *)
  #CL_ENGINE_DB_OPTIONS           ; uint32_t
  #CL_ENGINE_DB_VERSION           ; uint32_t
  #CL_ENGINE_DB_TIME              ; time_t
  #CL_ENGINE_AC_ONLY              ; uint32_t
  #CL_ENGINE_AC_MINDEPTH          ; uint32_t
  #CL_ENGINE_AC_MAXDEPTH          ; uint32_t
  #CL_ENGINE_TMPDIR               ; (char *)
  #CL_ENGINE_KEEPTMP              ; uint32_t
  #CL_ENGINE_BYTECODE_SECURITY    ; uint32_t
  #CL_ENGINE_BYTECODE_TIMEOUT     ; uint32_t
  #CL_ENGINE_BYTECODE_MODE        ; uint32_t
  #CL_ENGINE_MAX_EMBEDDEDPE       ; uint64_t
  #CL_ENGINE_MAX_HTMLNORMALIZE    ; uint64_t
  #CL_ENGINE_MAX_HTMLNOTAGS       ; uint64_t
  #CL_ENGINE_MAX_SCRIPTNORMALIZE  ; uint64_t
  #CL_ENGINE_MAX_ZIPTYPERCG       ; uint64_t
  #CL_ENGINE_FORCETODISK          ; uint32_t
  #CL_ENGINE_DISABLE_CACHE        ; uint32_t
  #CL_ENGINE_DISABLE_PE_STATS     ; uint32_t
  #CL_ENGINE_STATS_TIMEOUT        ; uint32_t
  #CL_ENGINE_MAX_PARTITIONS       ; uint32_t
  #CL_ENGINE_MAX_ICONSPE          ; uint32_t
  #CL_ENGINE_MAX_RECHWP3          ; uint32_t
  #CL_ENGINE_MAX_SCANTIME         ; uint32_t
  #CL_ENGINE_PCRE_MATCH_LIMIT     ; uint64_t
  #CL_ENGINE_PCRE_RECMATCH_LIMIT  ; uint64_t
  #CL_ENGINE_PCRE_MAX_FILESIZE    ; uint64_t
  #CL_ENGINE_DISABLE_PE_CERTS     ; uint32_t
  #CL_ENGINE_PE_DUMPCERTS         ; uint32_t
EndEnumeration



; general
#CL_SCAN_GENERAL_ALLMATCHES                  = $1  ; scan in all-match mode */
#CL_SCAN_GENERAL_COLLECT_METADATA            = $2  ; collect metadata (--gen-json) */
#CL_SCAN_GENERAL_HEURISTICS                  = $4  ; option To enable heuristic alerts */
#CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE        = $8  ; allow heuristic match To take precedence. */
#CL_SCAN_GENERAL_UNPRIVILEGED                = $10 ; scanner will Not have Read access To files. */

; parsing capabilities options
#CL_SCAN_PARSE_ARCHIVE                       = $1
#CL_SCAN_PARSE_ELF                           = $2
#CL_SCAN_PARSE_PDF                           = $4
#CL_SCAN_PARSE_SWF                           = $8
#CL_SCAN_PARSE_HWP3                          = $10
#CL_SCAN_PARSE_XMLDOCS                       = $20
#CL_SCAN_PARSE_MAIL                          = $40
#CL_SCAN_PARSE_OLE2                          = $80
#CL_SCAN_PARSE_HTML                          = $100
#CL_SCAN_PARSE_PE                            = $200

; heuristic alerting options
#CL_SCAN_HEURISTIC_BROKEN                    = $2    ; alert on broken PE And broken ELF files */
#CL_SCAN_HEURISTIC_EXCEEDS_MAX               = $4    ; alert when files exceed scan limits (filesize, max scansize, Or max recursion depth) */
#CL_SCAN_HEURISTIC_PHISHING_SSL_MISMATCH     = $8    ; alert on SSL mismatches */
#CL_SCAN_HEURISTIC_PHISHING_CLOAK            = $10   ; alert on cloaked URLs in emails */
#CL_SCAN_HEURISTIC_MACROS                    = $20   ; alert on OLE2 files containing macros */
#CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE         = $40   ; alert If archive is encrypted (rar, zip, etc) */
#CL_SCAN_HEURISTIC_ENCRYPTED_DOC             = $80   ; alert If a document is encrypted (pdf, docx, etc) */
#CL_SCAN_HEURISTIC_PARTITION_INTXN           = $100  ; alert If partition table size doesn't make sense */
#CL_SCAN_HEURISTIC_STRUCTURED                = $200  ; Data loss prevention options, i.e. alert when detecting personal information */
#CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL     = $400  ; alert when detecting social security numbers */
#CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED   = $800  ; alert when detecting stripped social security numbers */
#CL_SCAN_HEURISTIC_STRUCTURED_CC             = $1000 ; alert when detecting credit card numbers */
#CL_SCAN_HEURISTIC_BROKEN_MEDIA              = $2000 ; alert If a file does Not match the identified file format, works With JPEG, TIFF, GIF, PNG */

; mail scanning options
#CL_SCAN_MAIL_PARTIAL_MESSAGE                = $1

; dev options
#CL_SCAN_DEV_COLLECT_SHA                     = $1 ; Enables hash output in sha-collect builds - For internal use only */
#CL_SCAN_DEV_COLLECT_PERFORMANCE_INFO        = $2 ; collect performance timings */







Structure cl_scan_options
  general.l
  parse.l
  alert.l
  heuristic_alert.l
  mail.l
  dev.l
EndStructure


Structure cl_stat
  *dir
  *stattab
  *statdname
  entries.l
EndStructure


; Initialization
PrototypeC.i cl_init(options.l)
PrototypeC.i cl_engine_free(*engine)

; Database loading
PrototypeC.i cl_retdbdir()
PrototypeC.i cl_load(path.p-UTF8, *engine, *signo.Long, options.l)


; Error handling
PrototypeC.i cl_strerror(clerror.l)

; Engine structure
PrototypeC.i cl_engine_compile(*engine)

PrototypeC.i cl_engine_new()

; Limits
PrototypeC.i cl_engine_set_num(*engine, field.l, num.q)
PrototypeC.q cl_engine_get_num(*engine, field.l, *err.Long)
PrototypeC.i cl_engine_set_str(*engine, field.l, str.p-UTF8)
PrototypeC.i cl_engine_get_str(*engine, field.l, *err.Long)


; Database checks
PrototypeC.i cl_statinidir(dirname.p-UTF8, *dbstat.cl_stat)
PrototypeC.i cl_statchkdir(*dbstat.cl_stat)
PrototypeC.i cl_statfree(*dbstat.cl_stat)
PrototypeC.i cl_countsigs(*path.p-UTF8, countoptions.l, *sigs.Long)


; Data scan functions
PrototypeC.i cl_scanfile(file.p-UTF8, *virname, scanned, *engine, *option)
PrototypeC.i cl_scandesc(file.p-UTF8, *virname, scanned, *engine, *option)


Global cl_init.cl_init
Global cl_engine_free.cl_engine_free

Global _cl_retdbdir.cl_retdbdir
Global cl_load.cl_load

Global _cl_strerror.cl_strerror

Global cl_engine_compile.cl_engine_compile
Global cl_engine_new.cl_engine_new

Global cl_engine_set_num.cl_engine_set_num
Global cl_engine_get_num.cl_engine_get_num
Global cl_engine_set_str.cl_engine_set_str
Global _cl_engine_get_str.cl_engine_get_str

Global cl_statinidir.cl_statinidir
Global cl_statchkdir.cl_statchkdir
Global cl_statfree.cl_statfree
Global cl_countsigs.cl_countsigs

Global cl_scanfile.cl_scanfile
Global cl_scandesc.cl_scandesc




Procedure.i cl_LoadLibrary()
  
  Protected hDLL.i
  
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Windows
      hDLL = OpenLibrary(#PB_Any, "libclamav.dll")
    CompilerCase #PB_OS_Linux
      hDLL = OpenLibrary(#PB_Any, "/usr/lib/libclamav.so")
    CompilerCase #PB_OS_MacOS
      hDLL = OpenLibrary(#PB_Any, "/usr/lib/libclamav.dylib") ; not sure about this
  CompilerEndSelect
  If hDLL <> 0
    cl_init = GetFunction(hDLL, "cl_init")
    cl_engine_free = GetFunction(hDLL, "cl_engine_free")
    
    _cl_retdbdir = GetFunction(hDLL, "cl_retdbdir")
    cl_load = GetFunction(hDLL, "cl_load")
    
    _cl_strerror = GetFunction(hDLL, "cl_strerror")
    
    cl_engine_compile = GetFunction(hDLL, "cl_engine_compile")
    cl_engine_new = GetFunction(hDLL, "cl_engine_new")
    
    cl_engine_set_num = GetFunction(hDLL, "cl_engine_set_num")
    cl_engine_get_num = GetFunction(hDLL, "cl_engine_get_num")
    cl_engine_set_str = GetFunction(hDLL, "cl_engine_set_str")
    _cl_engine_get_str = GetFunction(hDLL, "cl_engine_get_str")
    
    cl_statinidir = GetFunction(hDLL, "cl_statinidir")
    cl_statchkdir = GetFunction(hDLL, "cl_statchkdir")
    cl_statfree = GetFunction(hDLL, "cl_statfree")
    cl_countsigs = GetFunction(hDLL, "cl_countsigs")
    
    cl_scanfile = GetFunction(hDLL, "cl_scanfile")
    cl_scandesc = GetFunction(hDLL, "cl_scandesc")
  EndIf
  
  ProcedureReturn hDLL
  
EndProcedure


Procedure.s cl_retdbdir()
  
  Protected DBDir$, *DBDir
  
  *DBDir = _cl_retdbdir()
  If *DBDir
    DBDir$ = PeekS(*DBDir, -1, #PB_UTF8)
  EndIf
  
  ProcedureReturn DBDir$
  
EndProcedure


Procedure.s cl_strerror(clerror.l)
  
  Protected Error$, *Error
  
  *Error = _cl_strerror(clerror)
  If *Error
    Error$ = PeekS(*Error, -1, #PB_UTF8)
  EndIf
  
  ProcedureReturn Error$
  
EndProcedure


Procedure.s cl_engine_get_str(*engine, field.l, *err.Long)
  
  Protected EngineStr$, *EngineStr
  
  *EngineStr = _cl_engine_get_str(*engine, field.l, *err.Long)
  If *EngineStr
    EngineStr$ = PeekS(*EngineStr, -1, #PB_UTF8)
  EndIf
  
  ProcedureReturn EngineStr$
  
EndProcedure




CompilerIf #PB_Compiler_IsMainFile
  
  Define.i hlib, retv
  Define.l scanned
  Define signo.Long
  Define *virname, *Engine
  Define dboptions.cl_scan_options
  
  hLib = cl_LoadLibrary()
  If hlib
    
    retv = cl_init(#CL_INIT_DEFAULT)
    If retv = #CL_SUCCESS
      
      Debug "DB Dir: " + cl_retdbdir()
      
      *Engine = cl_engine_new()
      If *Engine <> 0
        
        CompilerIf #PB_Compiler_OS = #PB_OS_Windows
          retv = cl_load(".", *Engine, signo, #CL_DB_STDOPT)
        CompilerElse
          retv = cl_load(cl_retdbdir() + "/.", *Engine, signo, #CL_DB_STDOPT)
        CompilerEndIf
        If retv = #CL_SUCCESS
          
          Debug "Loaded signatures: " + Str(signo\l)
          
          retv = cl_engine_compile(*Engine)
          If retv = #CL_SUCCESS
            retv = cl_scanfile("virus.dat", @*virname, @scanned, *Engine, @dboptions)
            ;Debug scanned
            If retv = #CL_VIRUS
              Debug "Found: " + PeekS(*virname, -1, #PB_UTF8)
            Else
              If retv = #CL_CLEAN
                Debug "No Virus"
              Else
                Debug "cl_scanfile failed: " + cl_strerror(retv)
              EndIf
            EndIf
          Else
            Debug "cl_engine_compile failed: " + cl_strerror(retv)
          EndIf
        Else
          Debug "cl_load failed: " + cl_strerror(retv)
        EndIf
      Else
        Debug "cl_engine_new failed"
      EndIf
      
      cl_engine_free(*Engine)
    Else
      Debug "cl_init failed: " + cl_strerror(retv)
    EndIf
  Else
    Debug "Was not able to load the lib"
  EndIf
  
CompilerEndIf
I use the EICAR test virus.

The dot in cl_load() points to the current directory (my code/execuable and the vcd files are in the same directory).
So main.cvd and daily.cvd are loaded
Loaded signatures: 8540729
Win.Test.EICAR_HDB-1
You need to select the action 'leave it' in MS defender :wink:
Else the test virus will be deleted before it can be scanned by the code.
Last edited by infratec on Fri Jul 02, 2021 10:51 am, edited 3 times in total.
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Clam Antivirus - wrapping the libclamav.dll

Post by infratec »

I extended the listing above.
vwidmer
Enthusiast
Enthusiast
Posts: 282
Joined: Mon Jan 20, 2014 6:32 pm

Re: Clam Antivirus - wrapping the libclamav.dll

Post by vwidmer »

@infratec

Works good for me other then had to change this line

Code: Select all

retv = cl_load(".", *Engine, signo, #CL_DB_STDOPT)
to

Code: Select all

retv = cl_load(cl_retdbdir() + "/.", *Engine, signo, #CL_DB_STDOPT)
DB Dir: /var/lib/clamav
Loaded signatures: 8541682
Found: Win.Test.EICAR_HDB-1


running on Manjaro Linux
WARNING: I dont know what I am doing! I just put stuff here and there and sometimes like magic it works. So please improve on my code and post your changes so I can learn more. TIA
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Clam Antivirus - wrapping the libclamav.dll

Post by infratec »

Unfortunately it seems that the windows version delivers a wrong result for
cl_retdbdir()

It returns the right directory + "\database"

Very strange.

I added your version to the code above.
Post Reply