#!/usr/bin/perl -w # vim: sw=4 et # this scripts expects its input to be a pcap dumpfile, with linktype '1' # - packets with ether+ip+udp headers # # see also # c:\local\projects\wince\synce\trunk\librapi2\src\README.rapi # c:\local\projects\wince\rapinet\rapi-wm5.txt # this script dumps a activesync protocol (windump) capture file # # for a disassembly of the .exe implementing this, see: # C:\local\phones\htc_tornado\os\1.5.331.4\files\rapiclnt.idb # ... utils: # print map { s/\x0a/\\n\n/g; s/\x0d/\\r/g; $_; } pack 'H*', '' # print pack 'H*', join "", map { sprintf("%X", $_-ord('A')) } unpack 'C*', '' # perl rapiudp.pl rapi_*.cap # 1: the pc does a dhcp request to the device for a ip addr. # DPT0: - dtpt_srv.dll handles the dhcp connect, reads config from: [HKLM\comm\dtpt] # it uses DHCPSRV to implement the dhcp protocol. # # 2: the device says: dev=169.254.2.1, pc=169.254.2.2 # 3: the device connects to pc-tcp port 5721 # 4: the pc sends '0x7f' to device-udp port 5679 every second, # until the device is done connecting and has authenticated the user on port 990 # 5: the device connects to pc-tcp port 990 # 6: the device sends '0' # possibe device requests: # 0 -> is answered by 3 # 6 -> reply : 7+as-version # 4 -> + id-packet, optionally answered with password # possible pc answers: # 1 -> device sends '2' # 3 -> the device sends '4' + the id packet # 5 -> must be followed by '4', and the id of the next connection. # 7 -> followed by 8, maj, min activesync version # also the pc may initiate, it then starts by sending '1' # 7: the device disconnects from port 5721 when communication of 990 has started # # id packet: # e1b7761a89ac81f0c2fa4f48471f8758 deviceguid # 05000000 01000000 osmaj osmin # 0a000000 69002d006d006100740065002000530050003500 0000 [HKLM\ident] Name # L"i-mate SP5" # 05014600 osmaj.osmin.buildnr # 110a0000 processortype # 05000000 sync flags # 00000000 partnerid_1 # 00000000 partnerid_2 # 0c000000 536d61727450686f6e65 0000 platformtype-list # "SmartPhone" # 0a000000 692d6d61746520535035 00 oeminfo # "i-mate SP5" # 01000000 nr of platformtype strings # 04000000 00000000 spi_0xe0 result # c1000600 [HKLM\ident] Pegid # when pegid!=0 a password is required. # decoding the password : # my @a= unpack("C*", pack("H*", "f0c1f3c1f2c1f5c1")); # print pack("C*",map { $a[$_*2]^$a[$_*2+1] } (0..@a/2-1)); # -> 1234 # ... actually it is xorred with the low byte of the pegid. # after receiving {5,4,id} # a new connection to port 990 is made, on which first the id is replied. # after which a loop processing commands is entered. # [length] [rapifn] [params] # # rapifunctions are usually called with (obj1, obj2) # except 0a,0b,0c and 4c: with (obj0, obj2) # my %CEDEVCAPS= ( 0=>'DRIVERVERSION', 2=>'TECHNOLOGY', 4=>'HORZSIZE', 6=>'VERTSIZE', 8=>'HORZRES', 10=>'VERTRES', 12=>'BITSPIXEL', 14=>'PLANES', 16=>'NUMBRUSHES', 18=>'NUMPENS', 20=>'NUMMARKERS', 22=>'NUMFONTS', 24=>'NUMCOLORS', 26=>'PDEVICESIZE', 28=>'CURVECAPS', 30=>'LINECAPS', 32=>'POLYGONALCAPS', 34=>'TEXTCAPS', 36=>'CLIPCAPS', 38=>'RASTERCAPS', 40=>'ASPECTX', 42=>'ASPECTY', 44=>'ASPECTXY', ); my %HIVES= ( 0x80000000=>'HKEY_CLASSES_ROOT', 0x80000001=>'HKEY_CURRENT_USER', 0x80000002=>'HKEY_LOCAL_MACHINE', 0x80000003=>'HKEY_USERS', 0x80000004=>'HKEY_PERFORMANCE_DATA', 0x80000050=>'HKEY_PERFORMANCE_TEXT', 0x80000060=>'HKEY_PERFORMANCE_NLSTEXT', 0x80000005=>'HKEY_CURRENT_CONFIG', 0x80000006=>'HKEY_DYN_DATA', ); my %REGTYPES= ( 0=>'REG_NONE', 1=>'REG_SZ', 2=>'REG_EXPAND_SZ', 3=>'REG_BINARY', 4=>'REG_DWORD', 5=>'REG_DWORD_BIG_ENDIAN', 6=>'REG_LINK', 7=>'REG_MULTI_SZ', 8=>'REG_RESOURCE_LIST', 9=>'REG_FULL_RESOURCE_DESCRIPTOR', 10=>'REG_RESOURCE_REQUIREMENTS_LIST', 11=>'REG_QWORD', ); my %rapi= ( 0x00=>{ api=>'CeEventHasOccured', request=>{packfmt=>'VV/a', field=>[qw(event msg)], format=>{msg=>\&decodeUnicode} }, response=>{packfmt=>'', field=>[qw()]} }, 0x01=>{ api=>'CeSyncTimeToPc', request=>{packfmt=>'a8VV', field=>[qw(t w v)]}, response=>{packfmt=>'', field=>[qw()]} }, 0x02=>{ api=>'CeStartReplication', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x03=>{ api=>'RegCopyFile', request=>{packfmt=>'V/a', field=>[qw(filename)], format=>{filename=>\&decodeUnicode}}, response=>{packfmt=>'', field=>[qw()]} }, 0x04=>{ api=>'RegRestoreFile', request=>{packfmt=>'V/a', field=>[qw(filename)], format=>{filename=>\&decodeUnicode}}, response=>{packfmt=>'', field=>[qw()]} }, 0x05=>{ api=>'GetPasswordActive', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x06=>{ api=>'SetPasswordActive', request=>{packfmt=>'VV/a', field=>[qw(active password)], format=>{password=>\&decodeUnicode}}, response=>{packfmt=>'', field=>[qw()]} }, 0x07=>{ api=>'SetPassword', request=>{packfmt=>'V/aV/a', field=>[qw(oldpw newpw)], format=>{oldpw=>\&decodeUnicode, newpw=>\&decodeUnicode}}, response=>{packfmt=>'', field=>[qw()]} }, 0x08=>{ api=>'GetSystemMemoryDivision', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x09=>{ api=>'SetSystemMemoryDivision', request=>{packfmt=>'V', field=>[qw(memdiv)]}, response=>{packfmt=>'', field=>[qw()]} }, 0x0a=>{ api=>'CeBackupDatabase', request=>{packfmt=>'V', field=>[qw(dboid)]}, # returns stream response=>{packfmt=>'', field=>[qw()]} }, 0x0b=>{ api=>'CeRestoreDatabase', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x0c=>{ api=>'CeBackupFile', request=>{packfmt=>'V/a', field=>[qw(filename)], format=>{filename=>\&decodeUnicode }}, response=>{packfmt=>'', field=>[qw()]} }, 0x0d=>{ api=>'activesync_function_13', request=>{packfmt=>'', field=>[qw()]}, # returns 2 dwords: {0x78, 0} response=>{packfmt=>'', field=>[qw()]} }, 0x0e=>{ api=>'CeProcessConfig', request=>{packfmt=>'V/aV', field=>[qw(request flag)], format=>{request=>\&decodeUnicode, flag=>\&decodeHex}}, response=>{packfmt=>'V/a', field=>[qw(reply)], format=>{reply=>\&decodeUnicode }} }, 0x0f=>{ api=>'activesync_function_7', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x10=>{ api=>'activesync_function_8', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x11=>{ api=>'CeFindFirstFile', request=>{packfmt=>'V/a', field=>[qw(filename)], format=>{filename=>\&decodeUnicode }}, #todo : properly decode a44 field response=>{packfmt=>'a44a*', field=>[qw(x foundfile)], format=>{x=>\&decodeBinary, foundfile=>\&decodeUnicode } }}, 0x12=>{ api=>'CeFindNextFile', request=>{packfmt=>'V', field=>[qw(handle)], format=>{handle=>\&decodeHex}}, response=>{packfmt=>'VVa8a8a8VVVa*', field=>[qw(reclen attrib t0 t1 t2 sizeH sizeL oid filename)], format=>{filename=>\&decodeUnicode, t0=>\&decodeBinary, t1=>\&decodeBinary, t2=>\&decodeBinary, oid=>\&decodeHex} } }, 0x13=>{ api=>'CeFindClose', request=>{packfmt=>'V', field=>[qw(handle)], format=>{handle=>\&decodeHex} }, response=>{packfmt=>'', field=>[qw()]} }, 0x14=>{ api=>'CeGetFileAttributes', request=>{packfmt=>'V/a', field=>[qw(filename)], format=>{filename=>\&decodeUnicode }}, response=>{packfmt=>'' , field=>[qw()]} }, 0x15=>{ api=>'CeSetFileAttributes', request=>{packfmt=>'V/aV', field=>[qw(filename attr)], format=>{filename=>\&decodeUnicode }}, response=>{packfmt=>'', field=>[qw()]} }, 0x16=>{ api=>'CeCreateFile', request=>{packfmt=>'V/aVVVV', field=>[qw(filename access sharemode createdisposition attributes)], format=>{filename=>\&decodeUnicode, access=>\&decodeHex }}, response=>{packfmt=>'', field=>[qw()]}, }, 0x17=>{ api=>'CeReadFile', request=>{packfmt=>'VV', field=>[qw(handle size)], format=>{handle=>\&decodeHex} }, response=>{packfmt=>'V/a', field=>[qw(data)], format=>{data=>\&decodeBinary} } }, 0x18=>{ api=>'CeWriteFile', request=>{packfmt=>'VV/a', field=>[qw(handle data)], format=>{handle=>\&decodeHex, data=>\&decodeBinary}}, response=>{packfmt=>'V', field=>[qw(byteswritten)], format=>{byteswritten=>\&decodeHex } } }, 0x19=>{ api=>'CeCloseHandle', request=>{packfmt=>'V', field=>[qw(handle)], format=>{handle=>\&decodeHex}}, response=>{packfmt=>'', field=>[qw()]} }, 0x1a=>{ api=>'CeFindAllFiles', request=>{packfmt=>'V/aV', field=>[qw(pattern flags)], format=>{pattern=>\&decodeUnicode }}, response=>{packfmt=>'Va*', field=>[qw(count data)], format=>{data=>\&decodeFindAll} } }, 0x1b=>{ api=>'CeFindFirstDatabase', request=>{packfmt=>'V', field=>[qw(type)]}, response=>{packfmt=>'', field=>[qw()]} }, 0x1c=>{ api=>'CeFindNextDatabase', request=>{packfmt=>'V', field=>[qw(handle)]}, response=>{packfmt=>'', field=>[qw()]} }, 0x1d=>{ api=>'CeOidGetInfo', request=>{packfmt=>'V', field=>[qw(oid)]}, response=>{packfmt=>'', field=>[qw()]} }, 0x1e=>{ api=>'CeCreateDatabase', request=>{packfmt=>'V/aVva*', field=>[qw(filename dbtype numorder ordering)]}, response=>{packfmt=>'', field=>[qw()]} }, 0x1f=>{ api=>'CeOpenDatabase', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x20=>{ api=>'CeDeleteDatabase', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x21=>{ api=>'CeReadRecordProps', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x22=>{ api=>'CeWriteRecordProps', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x23=>{ api=>'CeDeleteRecord', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x24=>{ api=>'CeSeekDatabase', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x25=>{ api=>'CeSetDatabaseInfo', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x26=>{ api=>'CeSetFilePointer', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x27=>{ api=>'CeSetEndOfFile', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x28=>{ api=>'CeCreateDirectory', request=>{packfmt=>'V/a', field=>[qw(dirname)], format=>{dirname=>\&decodeUnicode }}, response=>{packfmt=>'', field=>[qw()]} }, 0x29=>{ api=>'CeRemoveDirectory', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x2a=>{ api=>'CeCreateProcess', request=>{packfmt=>'V/aV/aV', field=>[qw(procname cmdline dword)], format=>{procname=>\&decodeUnicode, cmdline=>\&decodeUnicode }}, response=>{packfmt=>'V4', field=>[qw(hProc Procid hThread ThreadId)], format=>{hProc=>\&decodeHex, Procid=>\&decodeHex, hThread=>\&decodeHex, ThreadId=>\&decodeHex}} }, 0x2b=>{ api=>'CeMoveFile', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x2c=>{ api=>'CeCopyFile', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x2d=>{ api=>'CeDeleteFile', request=>{packfmt=>'V/a', field=>[qw(name)], format=>{name=>\&decodeUnicode}}, response=>{packfmt=>'', field=>[qw()]} }, 0x2e=>{ api=>'CeGetFileSize', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x2f=>{ api=>'CeRegOpenKeyEx', request=>{packfmt=>'VV/a', field=>[qw(hkey path)], format=>{hkey=>\&decodeHKey, path=>\&decodeUnicode }}, response=>{packfmt=>'V', field=>[qw(hkey)]} }, 0x30=>{ api=>'CeRegEnumKeyEx', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x31=>{ api=>'CeRegCreateKeyEx', request=>{packfmt=>'VV/aVv', field=>[qw(hkey path options samdesired)], format=>{hkey=>\&decodeHKey, path=>\&decodeUnicode }}, response=>{packfmt=>'VV', field=>[qw(hkey disposition)], format=>{hkey=>\&decodeHex} }}, 0x32=>{ api=>'CeRegCloseKey', request=>{packfmt=>'V', field=>[qw(hkey)], format=>{hkey=>\&decodeHKey}}, response=>{packfmt=>'', field=>[qw()]} }, 0x33=>{ api=>'CeRegDeleteKey', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x34=>{ api=>'CeRegEnumValue', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x35=>{ api=>'CeRegDeleteValue', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x36=>{ api=>'CeRegQueryInfoKey', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x37=>{ api=>'CeRegQueryValueEx', request=>{packfmt=>'VV/aV', field=>[qw(hkey name size)], format=>{hkey=>\&decodeHKey, name=>\&decodeUnicode}}, # todo: think of a way to decode the data based on the type response=>{packfmt=>'VV/a', field=>[qw(type data)], format=>{type=>\&decodeRegtype, data=>\&decodeBinary} } }, 0x38=>{ api=>'CeRegSetValueEx', request=>{packfmt=>'VV/aVV/a', field=>[qw(hKey name type data)], format=>{hkey=>\&decodeHKey, name=>\&decodeUnicode, type=>\&decodeRegtype, data=>\&decodeBinary} }, response=>{packfmt=>'', field=>[qw()]} }, 0x39=>{ api=>'CeGetStoreInformation', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'VV', field=>[qw(unknown1 unknown2)]} }, 0x3a=>{ api=>'CeGetSystemMetrics', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x3b=>{ api=>'CeGetDesktopDeviceCaps', request=>{packfmt=>'V', field=>[qw(nIndex)], format=>{nIndex=>sub { return decodeEnum(\%CEDEVCAPS, @_); } } }, response=>{packfmt=>'', field=>[qw()]} }, 0x3c=>{ api=>'CeFindAllDatabases', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x3d=>{ api=>'CeGetSystemInfo', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x3e=>{ api=>'CeSHCreateShortcut', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x3f=>{ api=>'CeSHGetShortcutTarget', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x40=>{ api=>'CeCheckPassword', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x41=>{ api=>'CeGetFileTime', request=>{packfmt=>'V', field=>[qw(handle)], format=>{handle=>\&decodeHex}}, response=>{packfmt=>'a8a8a8', field=>[qw(c m a)], format=>{c=>\&decodeBinary, m=>\&decodeBinary, a=>\&decodeBinary} } }, 0x42=>{ api=>'CeSetFileTime', request=>{packfmt=>'Va8a8a8', field=>[qw(handle c m a)], format=>{handle=>\&decodeHex, c=>\&decodeBinary, m=>\&decodeBinary, a=>\&decodeBinary}}, response=>{packfmt=>'', field=>[qw()]} }, 0x43=>{ api=>'CeGetVersionEx', request=>{packfmt=>'V', field=>[qw(unknown)]}, response=>{packfmt=>'VVVVVVv', field=>[qw(len cpu maj min build x y)]} }, 0x44=>{ api=>'CeGetWindow', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x45=>{ api=>'CeGetWindowLong', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x46=>{ api=>'CeGetWindowText', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x47=>{ api=>'CeGetClassName', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x48=>{ api=>'CeGlobalMemoryStatus', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x49=>{ api=>'CeGetSystemPowerStatusEx', request=>{packfmt=>'V', field=>[qw(unknown)]}, response=>{packfmt=>'VVVVV', field=>[qw(unk0 unk1 unk2 unk3 unk4)]} }, 0x4a=>{ api=>'CeGetTempPath', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x4b=>{ api=>'CeGetSpecialFolderPath', request=>{packfmt=>'VV', field=>[qw(id size)]}, response=>{packfmt=>'a*', field=>[qw(path)], format=>{path=>\&decodeUnicode} }}, 0x4c=>{ api=>'CeRapiInvoke', request=>{packfmt=>'VV/aV/aV/aV', field=>[qw(unknown dllname procname data withstream)], format=>{data=>\&decodeBinary, dllname=>\&decodeUnicode, procname=>\&decodeUnicode}}, response=>{packfmt=>'V/a', field=>[qw(reply)], format=>{reply=>\&decodeBinary } } }, 0x4d=>{ api=>'CeFindFirstDatabaseEx', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x4e=>{ api=>'CeFindNextDatabaseEx', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x4f=>{ api=>'CeCreateDatabaseEx', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x50=>{ api=>'CeSetDatabaseInfoEx', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x51=>{ api=>'CeOpenDatabaseEx', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x52=>{ api=>'CeDeleteDatabaseEx', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x53=>{ api=>'CeReadRecordPropsEx', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x54=>{ api=>'CeMountDBVol', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x55=>{ api=>'CeUnmountDBVol', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x56=>{ api=>'CeFlushDBVol', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x57=>{ api=>'CeEnumDBVolumes', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x58=>{ api=>'CeOidGetInfoEx', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x59=>{ api=>'CeSyncStart', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x5a=>{ api=>'CeSyncStop', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, 0x5b=>{ api=>'CeQueryInstructionSet', request=>{packfmt=>'V', field=>[qw(query)]}, response=>{packfmt=>'V', field=>[qw(current)]} }, 0x5c=>{ api=>'CeGetDiskFreeSpaceExW', request=>{packfmt=>'', field=>[qw()]}, response=>{packfmt=>'', field=>[qw()]} }, ); use strict; use IO::File; use Dumpvalue; use Getopt::Long; my $g_pktnr= 0; my $verbose= 0; GetOptions("v" => \$verbose); my $d= new Dumpvalue; $|=1; my %sessions; my $packetcount=0; for my $filename (@ARGV) { printf("%02d .. ", 1+scalar keys %sessions); processDumpfile($filename); printf("%02d %s\n", scalar keys %sessions, $filename); } sub processDumpfile { my $filename= shift; my $fh= IO::File->new($filename, "r") or die "$filename: $!\n"; binmode $fh; my $dumpheader; $fh->read($dumpheader, 0x18) or die "$filename: $!\n"; my ($magic, $version_major, $version_minor, $thiszone, $sigfigs, $snaplen, $linktype)= unpack("LSSLLLL", $dumpheader); if ($magic != 0xa1b2c3d4) { $fh->close(); die "not a libpcap dumpfile\n"; } if ($linktype!=1 && $linktype!=9) { die "unknown linktype $linktype\n"; } printf("cap file v%d.%d zone=%d figs=%x snaplen=%d linktype=%d\n", $version_major, $version_minor, $thiszone, $sigfigs, $snaplen, $linktype); while (!$fh->eof()) { my $pkthdr; $fh->read($pkthdr, 0x10) or die "$filename: $!\n"; my ($sec, $usec, $caplen, $wirelen)= unpack("LLLL", $pkthdr); #printf("%d.%d %d %d\n", $sec, $usec, $caplen, $wirelen); my $packet; $fh->read($packet, $caplen) or die "$filename: $!\n"; parsePacket($pkthdr, $packet, $linktype); } $fh->close(); } print "processed $packetcount packets\n"; for my $con (sort {$sessions{$a}{start}<=>$sessions{$b}{start}} keys %sessions) { my $ses= $sessions{$con}; printf("%s:%02d: connection-start: %s\n", $ses->{start}, $ses->{id}, $con); my $first990= 0; my %dirchar= (request=>'>', response=>'<'); for (my $i =0 ; $i<=$#{$ses->{request}{stream}} || $i<= $#{$ses->{response}{stream}} ; $i++) { for my $dir (qw(request response)) { next if $i>$#{$ses->{$dir}{stream}}; my $pkt= $ses->{$dir}{stream}[$i]; if ($con =~ /^tcp.*:990$/) { if ($i==0 && $pkt->{data} eq "\x00\x00\x00\x00") { # this is the initial session -> decode differently $first990=1; } # todo: recognize rapiinvoke streams if ($first990) { # todo: more descriptive decoding of initial session printf("%s:%02d%s %3d %4d %s\n", $pkt->{time}, $ses->{id}, $dirchar{$dir}, $pkt->{conix}, $pkt->{pktix}, decodeRapiSetupPacket($ses->{id}, $dir, $pkt->{data})); } else { printf("%s:%02d%s %3d %4d %s\n", $pkt->{time}, $ses->{id}, $dirchar{$dir}, $pkt->{conix}, $pkt->{pktix}, decodeRapiPacket($ses->{id}, $dir, $pkt->{data})); } } else { printf("%s:%02d%s %3d %4d %s\n", $pkt->{time}, $ses->{id}, $dirchar{$dir}, $pkt->{conix}, $pkt->{pktix}, unpack("H*", $pkt->{data})); } } } printf("%s:%02d_ connection-end: %s\n", $ses->{end}, $ses->{id}, $con) if ($ses->{end}); if ($con =~ /^tcp.*:990$/) { $first990= 0; } } exit(0); # parses packetstream into udp session sub parsePacket { my ($pkthdr, $pktdata, $linktype)= @_; my ($sec, $usec, $caplen, $elen)= unpack("LLLL", $pkthdr); my $pkttime= sprintf("%d.%06d", $sec, $usec); my $eth= $linktype==1 ? parseEther($pktdata) : $linktype==9 ? parsePpp($pktdata) : undef; return if (!$eth || $eth->{type} != 0x800); # ip my $ip= parseIp($eth->{data}); if ($ip->{proto} == 0x11) { my $udp= parseUdp($ip->{data}); return if ($udp->{dst}==137); return if ($udp->{dst}==1900); my $connection= "udp.".ipport($ip->{dst},$udp->{dst}) . ">" . ipport($ip->{src},$udp->{src}); my $direction= "response"; if (!exists $sessions{$connection}) { $connection= "udp.".ipport($ip->{src},$udp->{src}) . ">" . ipport($ip->{dst},$udp->{dst}); $direction= "request"; } if (!exists $sessions{$connection}{start}) { $sessions{$connection}{start}= $pkttime; $sessions{$connection}{count}= 0; $sessions{$connection}{id}= scalar keys %sessions; } if (length($udp->{data})>0) { push @{$sessions{$connection}{$direction}{stream}}, { data=>$udp->{data}, time=>$pkttime, pktix=>$g_pktnr++, conix=>$sessions{$connection}{count}++, }; } #printf("prev!tcp\n"); } elsif ($ip->{proto} == 6) { my $tcp= parseTcp($ip->{data}); my $connection= "tcp.".ipport($ip->{dst},$tcp->{dst}) . ">" . ipport($ip->{src},$tcp->{src}); my $svrport= $tcp->{src}; my $direction= "response"; if (!exists $sessions{$connection}) { $connection= "tcp.".ipport($ip->{src},$tcp->{src}) . ">" . ipport($ip->{dst},$tcp->{dst}); $svrport= $tcp->{dst}; $direction= "request"; } if (!exists $sessions{$connection}{start}) { $sessions{$connection}{start}= $pkttime; $sessions{$connection}{id}= scalar keys %sessions; } if ($tcp->{flags}{RST}) { $sessions{$connection}{end}= $pkttime; } if (length($tcp->{data})>0) { if (!$sessions{$connection}{$direction}{startseq}) { $sessions{$connection}{$direction}{startseq}= $tcp->{seq}; } my $seqnr= $tcp->{seq}-$sessions{$connection}{$direction}{startseq}; my $info= { data=>$tcp->{data}, time=>$pkttime, seq=>$seqnr, pktix=>$g_pktnr++, conix=>$sessions{$connection}{count}++, }; if ($svrport==990) { ($info->{length}, $info->{type}) = unpack("VV", $info->{data}); my $last= $sessions{$connection}{$direction}{stream}[-1]; if ($last && length($info->{data})>=8 && $last->{length}+4 >= length($last->{data})+length($info->{data}) && $info->{conix}-$last->{conix}==1 ) { # extend previous packet. # printf("extending %02d:%02d->%02d:%02d ll=%04x l(ld)=%04x with il=%04x l(id)=%04x i+l=%04x\n", # $last->{pktix},$last->{conix}, # $info->{pktix},$info->{conix}, # $last?$last->{length}:0, $last?length($last->{data}):0, # $info?$info->{length}:0, $info?length($info->{data}):0, # ($last?length($last->{data}):0)+($info?length($info->{data}):0)); $last->{data} .= $info->{data}; push @{$last->{time2}}, $info->{time}; $g_pktnr--; $sessions{$connection}{count}--; } else { # printf("prev: %02d:%02d->%02d:%02d ll=%04x l(ld)=%04x next: il=%04x l(id)=%04x\n", # $last?$last->{pktix}:0,$last?$last->{conix}:0, # $info->{pktix},$info->{conix}, # $last?$last->{length}:0, $last?length($last->{data}):0, # $info?$info->{length}:0, $info?length($info->{data}):0, # ($last?length($last->{data}):0)+($info?length($info->{data}):0)); push @{$sessions{$connection}{$direction}{stream}}, $info; } } else { push @{$sessions{$connection}{$direction}{stream}}, $info; } } } $packetcount++; } sub ipport { my ($ip, $port)= @_; return join(".", unpack "C*", pack "N", $ip).":$port"; #return sprintf("%08lx:%04x", $ip, $port); } sub parseEther { my ($data)= @_; my ($dst, $src, $type)= unpack("A6A6n", $data); return if (length($data)<14); return { src=>$src, dst=>$dst, type=>$type, data=>substr($data, 14), }; } sub parsePpp { my ($data)= @_; if ($data =~ /^\xff\x03\x00\x21/) { return { src=>"000000", dst=>"111111", type=>0x800, data=>substr($data, 4) }; } else { printf("unknown ppp header: %s\n", unpack("H*", substr($data, 0, 4))); return undef; } } # rfc 791 # 0 1 2 3 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # |Version| IHL |Type of Service| Total Length | CCn -> vh, tos, iplen # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Identification |Flags| Fragment Offset | nn -> id, flags # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Time to Live | Protocol | Header Checksum | CCn -> ttl, proto, csum # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Source Address | N -> src # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Destination Address | N -> dst # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Options | Padding | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ sub parseIp { my ($data)= @_; my ($vh, $tos, $iplen, $id, $flags, $ttl, $proto, $csum, $src, $dst)= unpack("CCnnnCCnNN", $data); my $version= int($vh/16); my $hlen= $vh & 15; my $ip= { src=>$src, dst=>$dst, version=>$version, hlen=>$hlen, tos=>$tos, len=>$iplen, id=>$id, offset=>($flags&0x1fff), flags=>{ ($flags&0x8000)?(reserved=>1):(), ($flags&0x4000)?(dontfragment=>1):(), ($flags&0x2000)?(morefragments=>1):(), }, ttl=>$ttl, proto=>$proto, csum=>$csum, options=>substr($data, 20, $hlen*4-20), data=>substr($data, $hlen*4, $iplen-$hlen*4), }; } # rfc 793: # 0 1 2 3 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Source Port | Destination Port | nn -> src, dst # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Sequence Number | N -> seq # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Acknowledgment Number | N -> ack # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Data | |U|A|P|R|S|F| | # | Offset| Reserved |R|C|S|S|Y|I| Window | nn -> flags, window # | | |G|K|H|T|N|N| | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Checksum | Urgent Pointer | nn -> csum, urg # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Options | Padding | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | data | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ sub parseTcp { my ($data)= @_; my ($src, $dst, $seq, $ack, $flags, $window, $csum, $urg)= unpack("nnNNnnnn", $data); my $hlen= int($flags>>12); my $tcp= { src=>$src, dst=>$dst, seq=>$seq, ack=>$ack, hlen=>$hlen, reserved=>($flags>>6)&0x3f, flags=>{ ($flags&0x01)?(FIN=>1):(), ($flags&0x02)?(SYN=>1):(), ($flags&0x04)?(RST=>1):(), ($flags&0x08)?(PSH=>1):(), ($flags&0x10)?(ACK=>1):(), ($flags&0x20)?(URG=>1):(), }, win=>$window, csum=>$csum, urg=>$urg, options=>substr($data, 20, $hlen*4-20), data=>substr($data, $hlen*4), }; $tcp->{urg}= $tcp->{flags}& return $tcp; } # rfc 768: # sub parseUdp { my ($data)= @_; my ($src, $dst, $ulen, $csum)= unpack("nnnn", $data); return { src=>$src, dst=>$dst, ulen=>$ulen, csum=>$csum, data=>substr($data, 8), }; } # 2.1<2.2 # [dw,dw,dw,dw] 0x07 + 0x08 + 0x04 + 0x05 (cmd, len, vermajor, verminor) # [dw] 0x03 # [dw,dw,dw] 0x05 + 0x04 + 0x12 (cmd, len, id) # 2.1>2.2 # [dw] 0x00 # [dw] 0x06 # [dw,dw,...] 0x04, 0x78, .... id-packet sub decodeRapiSetupPacket { my ($sesid, $dir, $data)= @_; if (length($data)==4) { return sprintf("%08lx", unpack("V", $data)); } else { return unpack("H*", $data); } } # request packfmt: # DWORD size # DWORD request # BYTE content[] # # response packfmt: # DWORD size # DWORD lasterror # DWORD result # BYTE data[] my %requesttype; sub decodeRapiPacket { my ($sesid, $dir, $data)= @_; if ($dir eq "request") { $dir="response"; } else { $dir="request"; } return unpack("H*", $data) if ($dir eq "request" && length($data)<8); return unpack("H*", $data) if ($dir eq "response" && length($data)<12); if ($dir eq "request") { my ($len, $type)= unpack("VV", $data); $requesttype{$sesid}= $type; if ($len!=length($data)-4) { printf("mismatch: len=%04x datal=%04x\n", $len, length($data)-4); } printf("l=%04x data:%s\n", length($data)-8, unpack("H*", substr($data, 8))) if ($verbose); if (exists $rapi{$type} && $rapi{$type}{$dir}{packfmt} && length($data)>=8) { my @fields= eval { unpack($rapi{$type}{$dir}{packfmt}.'a*', substr($data, 8)) }; if ($@) { warn "WARNING: $@\n"; return unpack("H*", $data); } if (length($fields[-1])) { printf("EXTRADATA: %s\n", unpack("H*", $fields[-1])); } # todo: change this in a two step process ( also in rs1 ) # first create hash of fields # then decode each field, passing the hash as second param for supplementary info to the decoder. return sprintf("rq1 %s %s", $rapi{$type}{api}, join(", ", map { my $fieldname= $rapi{$type}{$dir}{field}[$_]; #print("!! t=$type d=$dir fld=$fieldname fmt=$rapi{$type}{$dir}{format}{$fieldname}\n"); sprintf("%s=%s", $fieldname, (exists $rapi{$type}{$dir}{format}{$fieldname}) ? $rapi{$type}{$dir}{format}{$fieldname}($fields[$_]) : $fields[$_]) } (0..$#fields-1))); } else { # 'rq2' => unknown request return sprintf("rq2 %08lx %08lx %s %s", $len, $type, exists $rapi{$type} ? $rapi{$type}{api} : "-", length($data)>8 ? unpack("H*", substr($data, 8)) : "-"); } } else { my ($len, $error, $returnval)= unpack("VVV", $data); if ($len!=length($data)-4) { printf("mismatch: len=%04x datal=%04x\n", $len, length($data)-4); } my $type= $requesttype{$sesid}; if (defined $type && exists $rapi{$type} && $rapi{$type}{$dir}{packfmt} && length($data)>=12) { my @fields= unpack($rapi{$type}{$dir}{packfmt}.'a*', substr($data, 12)); if (length($fields[-1])) { printf("EXTRADATA: %s\n", unpack("H*", $fields[-1])); } return sprintf("rs1 %08lx %08lx %s %s", $error, $returnval, $rapi{$type}{api}, join(", ", map { my $fieldname= $rapi{$type}{$dir}{field}[$_]; #print("!! t=$type d=$dir fld=$fieldname fmt=$rapi{$type}{$dir}{format}{$fieldname}\n"); sprintf("%s=%s", $fieldname, (exists $rapi{$type}{$dir}{format}{$fieldname}) ? $rapi{$type}{$dir}{format}{$fieldname}($fields[$_]) : $fields[$_]) } (0..$#fields-1))); } elsif (defined $type) { # 'rs2' => no decoding format defined return sprintf("rs2 %08lx %08lx %08lx %s %s", $len, $error, $returnval, $rapi{$type}{api}, length($data)>12 ? unpack("H*", substr($data, 12)) : "-"); } else { # 'rs3' => unknown request return sprintf("rs3 %08lx %08lx %08lx %s", $len, $error, $returnval, length($data)>12 ? unpack("H*", substr($data, 12)) : "-"); } } } sub decodeUnicode { my $x= pack 'C*', unpack 'v*', shift; $x =~ s/\x00+$//; return "\"$x\""; } sub decodeAscii { my $x= shift; $x =~ s/\x00+$//; return $x; } sub decodeHex { sprintf("%08lx", shift); } sub decodeBinary { unpack("H128", $_[0]).(length($_[0])>128 && "..."); } sub decodeGuid { return sprintf("%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", unpack("VvvC*", shift)); } sub decodeEnum { if (!exists $_[0]{$_[1]}) { return sprintf("%08lx", $_[1]); } return $_[0]{$_[1]}; } sub decodeHKey { return decodeEnum(\%HIVES, @_); } sub decodeRegtype { return decodeEnum(\%REGTYPES, @_); } sub decodeFindAll { my $ofs= 0; my @list; while ($ofs